Advanced Graphics and Data Visualization in R

Lecture 01: “R”-efresher on R and best practices


0.1.0 An overview of Advanced Graphics and Data Visualization in R

“Advanced Graphics and Data Visualization in R” is brought to you by the Centre for the Analysis of Genome Evolution & Function’s (CAGEF) bioinformatics training initiative. CSB1021 was developed to enhance the skills of students with basic backgrounds in R by focusing on available philosophies, methods, and packages for plotting scientific data. While the datasets and examples used in this course will be centred on SARS-CoV-2 datasets, the techniques learned herein will be broadly applicable.

This lesson is the first in a 6-part series. The aim for the end of this series is for students to recognize how to import, format, and display data based on their intended message and audience. The format and style of these visualizations will help to identify and convey the key message(s) from their experimental data.

The structure of the class is a code-along style in Jupyter notebooks. At the start of each lecture, skeleton versions of the lecture will be provided for use on the University of Toronto Jupyter Hub so students can program along with the instructor.


0.2.0 Lecture objectives

This week will be your crash-course on Jupyter notebooks and R to refresh on packages and principles that will be relevant throughout our course. In our lectures and your assignments we will be working with some uncurated data to simulate the full experience of working with data from start to finish. It’s important that we are all familiar with, and understand the majority of the tidy data methods that we’ll be using in class so that we can focus on the new material as it appears. We’ll use some standard packages and practices to finesse our data before visualizing it, so let’s R-efresh ourselves.

At the end of this lecture we will have covered the following topics:

  1. Working with Jupyter notebooks and best coding practices.
  2. R data types, objects and working with them.
  3. Long-format and tidy data principles using the tidyverse package.
  4. Basic control flow and plotting.

0.3.0 A legend for text format in Jupyter markdown

grey background - a package, function, code, command or directory. Backticks are also use for in-line code.
italics - an important term or concept or an individual file or folder
bold - heading or a term that is being defined
blue text - named or unnamed hyperlink

... - Within each coding cell this will indicate an area of code that students will need to complete for the code cell to run correctly.

Blue box: A key concept that is being introduced

Yellow box: Risk or caution

Green boxes: Recommended reads and resources to learn Python

Red boxes: A comprehension question which may or may not involve a coding cell. You usually find these at the end of a section.


0.4.0 Lecture and data files used in this course

0.4.1 Weekly Lecture and skeleton files

Each week, new lesson files will appear within your JupyterHub folders. We are pulling from a GitHub repository using this Repository git-pull link. Simply click on the link and it will take you to the University of Toronto JupyterHub. You will need to use your UTORid credentials to complete the login process. From there you will find each week’s lecture files in the directory /2024-03-Adv_Graphics_R/Lecture_XX. You will find a partially coded skeleton.rmd file as well as all of the data files necessary to run the week’s lecture.

Alternatively, you can download the R-Markdown Notebook (.Rmd) and data files from the RStudio server to your personal computer if you would like to run independently of the Toronto tools.

0.4.2 Live-coding HTML page

A live lecture version will be available at camok.github.io that will update as the lecture progresses. Be sure to refresh to take a look if you get lost!

0.4.3 Post-lecture PDFs and Recordings

As mentioned above, at the end of each lecture there will be a completed version of the lecture code released as a PDF file under the Modules section of Quercus.

0.4.4 Data used in this lesson

Today’s datasets will focus on epidemiological data from the Ontario provincial government found here and here.

0.4.4.1 Dataset 1: Ontario_daily_change_in_cases_by_phu.csv

This dataset was obtained from the Ontario provincial website and holds statistics regarding SARS-CoV-2 cases throughout different public health units in the province. It is in a comma separate format and has been collected since 2020-03-24 through 2023-01-25.

0.4.4.2 Dataset 2: region_hospital_icu_covid_data.csv

This dataset was obtained from the Ontario provincial website and holds data regarding SARS-CoV-2 throughout 5 Ontario health regions. It is in a comma-separated format and has been growing/expanding since initial tracking started on 2020-04-02 through 2023-02-23. This is a dataset only tracks 7 variables specifically regarding the daily totals of hospitalized COVID-19 patients both in the ICU and general care.


0.5.0 Packages used in this lesson

tidyverse which has a number of packages including dplyr, tidyr, stringr, forcats and ggplot2

viridis helps to create color-blind palettes for our data visualizations

lubridate and zoo are helper packages used for working with date formats in R

Let’s run our first code cell!

# Packages to help tidy our data
library(tidyverse)

# Packages for the graphical analysis section
library(viridis)

# packages used for working with/formating dates in R
library(lubridate)
library(zoo)

1.0.0 Coding in R Markdown Notebooks

Work with your R markdown notebook on the University of Toronto datatools hub will all be contained within a new browser tab with the address bar showing something similar to

https://r.datatools.utoronto.ca/user/calvin.mok@utoronto.ca/rstudio/

All of this is running remotely on a University of Toronto server rather than your own machine.

You’ll see a directory structure from your home folder:

ie /home/rstudio/2024-03-Adv_Graphics_R/ and a folder to Lecture_01_R_Introduction within. Clicking on that, you’ll find Lecture_01.R-efresher.skeleton.Rmd which is the notebook we will use for today’s code-along lecture.


1.1.0 Why is this class using R Markdown Notebooks?

We’ve implemented the class this way to reduce the burden of having to install various programs. While installation can be a little tricky, it’s really not that bad. For this course, however, you don’t need to go through all of that just to improve on your data visualization skills.

R markdown notebooks also give us the option of inserting “markdown” text much like what you’re reading at this very exact moment. So we can intersperse ideas and information between our learning code blocks.

There is, however an appendix section at the end of this lecture detailing how to install the R-kernel itself and the integrated development environment (IDE) called RStudio.


1.2.0 Packages contain useful functions that we’ll use often

So… what are in these packages? A package can be a collection of - functions - data objects - compiled code - functions that override base functions in R

Functions are the basic workhorses of R; they are the tools we use to analyze our data. Each function can be thought of as a unit that has a specific task. A function takes an input, evaluates it using an expression (e.g. a calculation, plot, merge, etc.), and returns an output (a single value, multiple values, a graphic, etc.).

In this course we will frequently rely on a package called tidyverse which is also composed of a series of other packages we can use to reformat our data like readr, dplyr, tidyr and stringr.


1.3.0 R markdown notebooks run the programming language R

Behind the scenes of each markdown notebook the R kernel is running. As we move from code cell to new code cell, all of the variables or objects we have created are stored within memory. We can refer to these as we run the code and move forward but if you overwrite or change them by mistake, you may to have rerun multiple cell blocks!

There are some options in the “Code” menu that can alleviate these problems such as “Run Region > Run All Chunks Above”. If you think you’ve made a big error by overwriting a key object, you can use that option to “re-initialize” all of your previous code!

The run order of your code is also visible at the side of each code cell as [x]. When a code cell is still actively running it will be denoted as [*] since a number cannot be assigned to it. You’ll also notice your kernel (top right of the menu bar) has a small circle that will be dark while running, and clear while idle.

Remember these friendly keys/shortcuts:

  • Arrow keys to navigate up and down (and within a cell)
  • Ctrl+Shift+Enter to run a cell (both code and markdown)
  • Alt+Ctrl+Enter to run the next cell
  • Ctrl+Shift+C to quickly comment and uncomment single or multiple lines of code
  • Tab can be used while coding to autocomplete variable, function and file names, and even look at a list of possible parameters for functions.
  • Ctrl+Alt+I to insert a new coding cell

1.3.3 Why would you want to use a Markdown Notebook?

Depending on your needs, you may find yourself doing the following:

  • Analysing data for your project using available packages
  • Re-analysing data for your project
  • Analysing multiple datasets for your project
  • Collaborating on data and analyses for your project
  • Explaining your data and analyses to a supervisor or collaborator!

Markdown allows you to alternate between “markdown” notes and “code” that can be run or re-run on the fly.

Each data run and it’s results can be saved individually as a new notebook to compare data and small changes to analyses!

1.3.4 What is markdown language?

Markdown is a markup language that lets you write HTML and Java Script code in combination with other languages. This allows you to make html, pdf, and text documents that are combinations of text and code, enhancing reproducibility, a key aspect in scientific work. Having everything in a single place also boosts productivity during results interpretation - no need to go back and forth between tabs, pages, and documents. They can all be integrated in a single document, allowing for a more fluid narrative of the story that you are communicating to your audience (less distractions for you!). For example, the lines of code below and the text you are reading right now were created in R’s Markdown language. (Do not worry about the R code just yet. We will get there sooner than you think).

As mentioned, markdown also allows you to write in LaTeX, a document preparation system to write mathematical notation. All it takes is to wrap LaTeX code between single dollar signs ($) for inline notation or two double dollar signs ($$), one at the beginning of the equation and one at the end. For example, the equation Yi = beta0 + beta1 xi + epsilon_i, i=1, …, N can be transformed into LaTeX code by adding some characters: ***Y_i = _0 + _1 x_i + _i, i=1, , N***. Now, if we use $$ before and after the LaTeX code, this is what we get:

\[ Y_i = \beta_0 + \beta_1 x_i + \varepsilon_i, i=1, \dots,N \]

See? Just like that! Here is an example of a table made in Markdown, showing some of the most popular R libraries for data science:

Library Use
tidyverse Simplified tabular-data processing functions
ggplot2 Data visualization package typically included in the tidyverse
shiny Used to create interactive R-based web pages and interfaces
car Popular statistical analysis with Type II and III ANOVA tables

These are just a few examples of what you can do with Jupyter and Markdown. To find out more on how to get the best of Markdown, head on over to the [R Markdown cookbook] (https://bookdown.org/yihui/rmarkdown-cookbook/).

Once you are finished writing your code and interpreting those results in a markdown notebook, you can render the notebook into pdf, html, and many other formats. There are several ways to achieve this. The easiest option is to go to File > Knit Document. Afterwards there should be an option to view in browser at which point you can save as an HTML or print it to PDF.


1.4.0 Following best practices for coding will make life easier

Let’s discuss some important behaviours before we begin coding: - Code annotation (commenting) - Variable naming conventions - Best practices

1.4.1 Annotate your code with the # symbol

Why bother?

  • “Can you rerun this analysis and but change X parameter?” - Anonymous PI
  • “Can you make this plot, but with dashed lines, a different axis, with error bars?” - Anonymous labmate
  • “Can I borrow your code?” - Anonymous collaborator or officemate or PI
  • “Why is that object being sent to that function? What is it returning?” - You, Me, and anyone reading your code

Your worst collaborator is potentially you in 6 days or 6 months. Do you remember what you had for breakfast last Tuesday?

Credit: https://www.testbytes.net/blog/programming-memes/

You can annotate your code for selfish reasons, or altruistic reasons, but annotate your code.

How do I start?

  • It is, in general, part of best coding practices to keep things tidy and organized.

  • A hash-tag # will comment your text. Inside a code cell in a Jupyter Notebook or anywhere in an R script, all text after a hashtag will be ignored by R and by many other programming languages. It’s very useful to add comments about changes in your code, as well as detailed explanations about your scripts.

  • Put a description of what you are doing near your code at every process, decision point, or non-default argument in a function. For example, why you selected k=6 for an analysis, or the Spearman over Pearson option for your correlation matrix, or quantile over median normalization, or why you made the decision to filter out certain samples.

  • Break your code into sections to make it readable. Scripts are just a series of steps and major steps should be titled/outlined with your reasoning - much like when presenting your research.

  • Give your objects informative object names that are not the same as function names.

Comments may/should appear in three places:

  • At the beginning of your code: What’s the objective of your script?
  • Above every function you create: Why did you have to write your own function versus those that are already available in package x?
  • In-line or in-between lines of code: Why did you write that piece of code? What does it do? Why did you change a function’s defaults? ***
# Example commenting section
# At the beginning of the script, describing the purpose of your script and what you are trying to solve

bedmasAnswer <- 5 + 4 * 6 - 0 #In line: Describing a part of your code that is not obvious what it is for. 

#---------- Section dividers helps organize code structure ----------#
## Feel free to add extra hash tags to visually separate or emphasize comments

Maintaining well-documented code is also good for mental health!


1.4.2 Naming conventions for files, objects, and functions in R

  • Cannot start with a number
  • Cannot contain spaces or special characters in the name
  • Avoid naming your variables using names already used by R (for, next, while, etc.).
  • Consider appending the object type to your variable name (data frame = df, list = list, etc.)

Stylistically, you have the following options:

  • All lower case: e.g. myfirstobject
  • Period separated: e.g. my.first.object
  • Underscore separated: e.g. my_first_object
  • camelCase1: e.g. myFirstObject
  • CamelCase2: e.g. MyFirstObject (Usually reserved for Class names)

The most important aspects of naming conventions are being concise and consistent! Throughout this course you’ll see a hybrid system that uses the underscore to separate words but a period right before denoting the object type ie this_data.object.


1.4.3 Best Practices for Writing Scripts

  • Start each script with a description of what it does.

  • Then load all required packages.

  • Consider what working directory you are in when sourcing a script.

  • Use comments to mark off sections of code.

  • Put function definitions at the top of your file, or in a separate file if there are many.

  • Name and style code consistently.

  • Break code into small, discrete pieces.

    • This is more easily accomplished when working with different code cells in Jupyter Notebook.
  • Factor out common operations rather than repeating them.

  • Keep all of the source files for a project in one directory and use relative paths to access them.

    • Using relative paths close or inside your script’s director makes it easier to package or move around your scripts too.
  • Keep track of the memory used by your program.

  • Always start with a clean environment instead of saving the workspace.

  • Keep track of session information in your project folder.

  • Have someone else review your code.

  • Use version control.

For more information on best coding practices, please visit swcarpentry


1.5.0 Trouble-shooting basics

We all run into problems. We’ll see a lot of mistakes happen in class too! That’s OK if we can learn from our errors and quickly (or eventually) recover.

1.5.1 Determine the location and type of error

Usually when R generates an error it will produce some information about what has happened. This usually includes an error message detailing the kind of error it encountered or an error message generated by the function. It can also include a line where the error was encountered, or the name of the last function that was called before the error was encountered.

1.5.2 Common errors

  • file does not exist: Use getwd() to check where you are working, typelist.files() or the Files pane to check that your file exists there, and setwd() to change your directory if necessary. Preferably, work inside an R project with all project-related files in that same folder. Your working directory will be set automatically when you open the project (this can be done by using File -> New Notebook and following prompts).

  • typos: R is case sensitive so always check that you’ve spelled everything right. Get used to using the tab-autocompletion feature when possible. This can reduce typos and increase your overall programming speed.

  • open quotes, parentheses, brackets:

    • Jupyter Notebooks highlight the current cursor-denoted bracket set in \(\color{green}{\text{green}}\). If the bracket is unmatched on either side, it will be \(\color{red}{\text{red}}\).
  • data type: Use commands like typeof() and class() to check what type of data you have. Use str() to peak at your data structures if you’re making assumptions about it.

  • unexpected answers: To access the help menu, type help("function"), ?function (using the name of the function that you want to check), or help(package = "package_name").

    • Jupyter Notebooks: the result will come from a pop-up window on the bottom of the notebook.
  • function not found: Make sure the package name is properly spelled, installed, AND loaded. Libraries can be loaded to the environment using the function library("package_name"). If you only need one function from a package, or need to specify to what package a function belongs because there are functions with the same name that belong to different packages, you can use a double colon, i.e. package_name::function_name.

  • the R bomb!!: The session aborted can happen for a variety of reasons, like not having enough computational power to perform a task or also because of a system-wide failure.

    • Jupyter Notebooks: restart the Kernel from the menu or by double-tapping 0. You will need to rerun your previous cells!
  • cheatsheets: Meet your new best friends: cheatsheets!


1.5.3 Finding answers online

  • 99% of the time, someone has already asked your question

  • Google, Stack overflow, R Bloggers, SEQanswers, Quora, ResearchGate, RSeek, twitter, even reddit

  • Including the program, version, error, package and function helps, so be specific. Sometimes it is useful to include your operating system and version (Windows 10, Ubuntu 18, Mac OS 10, etc.).

  • You may run into assignment questions where the tools I’ve provided in lecture are not enough to reproduce the example output exactly as provided. If you wish to go that extra mile you may need to look for answers elsewhere by consulting references from the class or searching for it yourself. The truth is out there!

1.5.3.1 Asking a question in an online forum

  • Summarize your question in the title (be concise and objective!).
  • Introduce your question, how you ran into the problem, and how you tried to solve it yourself. If you haven’t done the bolded thing, do the bolded thing.
  • Show enough of your code and data for others to try to reproduce the problem/error.
  • Add tags that match your problem.
  • Respond to the feedback and vote for the answered that you picked. People put in their free time to answer and help you.
  • Take a look at StackOverflow’s tips on how to ask questions, as well as CRAN’s

Remember: Everyone looks for help online ALL THE TIME. It is very common. Also, with programming there are multiple ways to come up with an answer, even different packages that let you do the same thing in different ways. You will work on refining these aspects of your code as you go along in this course and in your coding career.

Last but not least, to make life easier: Under the Help pane, there is a cheatsheet of Jupyter notebook keyboard shortcuts or a browser list here.


2.0.0 Foundations of R

There are many tips and tricks to remember about R but here we’ll quickly recall some foundational knowledge that could be relevant in later lectures.

2.1.0 Assigning variables

If we want to hold on to a number, calculation, or object we need to assign it to a named variable. R has multiple methods for assigning a value to a variable and an order of precedence!

-> and ->> Rightward assignment: we won’t really be using this in our course.

<- and <<- Leftward assignment: assignment used by most ‘authentic’ R programmers but really just a historical keyboard throwback.

= Leftward assignment: commonly used token for assignment in many other programming languages but holds dual meaning!

Notes

  • In R, the assignment of a variable does not produce any standard output.

  • R processes at each new line unless you use a semicolon (;) to separate commands. This applies to assignment as well. One exception being when your function calls are spaced across lines and contained within the ().

  • R calculates the right side of the assignment first the result is then applied to the left.

    • This is a common paradigm in programming that simplifies variable behaviours for counting and tracking results as they build up over time. This also allows us to increment variables or manipulate objects to update them!

2.2.0 Data types are the basic building blocks of R

Data types are used to classify the basic spectrum of values that are used in R. Here’s a table describing some of the common data types we’ll encounter.

Data type Description Example
character Can be single or multiple characters (strings) of letters and symbols. Assigned using double ' or " a#c&E
integer Whole number values, either positive or negative 1
double Any number that is not an integer 7.5
logical Also known as a boolean, representing the state of a conditional (question) TRUE or FALSE
NA Represents the value of “Not Available” usually seen when imported data has missing values NA

2.2.1 Data structures hold single or multiple values

The job of data structures is to “host” the different data types. There are five basic types of data structures that we’ll use in R:

Data structure Dimensions Restrictions
vector 1D Holds a single data type
matrix 2D Holds a single data type
array nD Holds a single data type
data frame 2D Holds multiple data types with some restrictions
list 1D (technically) Holds multiple data types AND structures

Sometimes it is helpful to imagine Data Structures as real-world objects to understand how they are shaped and related to each other.


2.2.2 Vectors are like a queue of a single data type

  • Also known as atomic vectors, each element within a vector must be of the same data type: logical, integer, double, character, complex, or raw.

  • For each vector there are two key properties that can be queried with typeof() and length().

  • There is a numerical order to a vector, much like a queue AND you can access each element (piece of data) individually or in groups. Elements are ordered from 1 to length(your_vector) and can be accessed with an indexing operator []

  • Elements of a vector may be named, to facilitate subsetting by character vectors.

  • Elements of a vector may be subset by a logical vector.

# Build a character vector
char.vector <- c("Canada", ..., "Great Britain")
Error in eval(expr, envir, enclos): '...' used in an incorrect context
char.vector
Error in eval(expr, envir, enclos): object 'char.vector' not found
# subset by a single value
char.vector[...]
Error in eval(expr, envir, enclos): object 'char.vector' not found
# subset by multiple values
char.vector[...]
Error in eval(expr, envir, enclos): object 'char.vector' not found
# subset by removing values (cannot be mixed with positive values)
char.vector[c(-1, ...)]
Error in eval(expr, envir, enclos): object 'char.vector' not found
# subset with repeating multiple values
char.vector[c(1, 2, 3, ...)]
Error in eval(expr, envir, enclos): object 'char.vector' not found
# Build a named character vector by including variable names
character.vector <- c(a = ..., b = "United States", c = "Great Britain")
character.vector

# subset by element name
character.vector[c("a", ...)]

# subset by an explicit vector of logicals
character.vector[c(...)]

# Or subset by an implicit vector of logicals
character.vector[character.vector ...]
Error: <text>:12:35: unexpected symbol
11: # Or subset by an implicit vector of logicals
12: character.vector[character.vector ...
                                      ^

2.2.2.1 Coercion changes data from one type to another (where applicable)

R will implicitly force (coerce) your vector to be of one data type. In this case, the type that is most inclusive is a character vector. When we explicitly coerce a change from one data type to the next, it is known as casting. You can cast between certain data types and also object types.

  • Type-casting examples: as.logical(), as.integer(), as.double(), as.numeric(), as.character(), and as.factor()

  • Structure casting examples: as.data.frame(), as.list(), and as.matrix()

Importantly, when coercing, the R kernel converts from more specific to general types usually in this order:


logical \(\rightarrow\) integer \(\rightarrow\) numeric \(\rightarrow\) complex \(\rightarrow\) character \(\rightarrow\) list.

# Make a logical vector and display its structure
logical.vector <- c(TRUE, FALSE, TRUE, FALSE, FALSE)
str(logical.vector)
 logi [1:5] TRUE FALSE TRUE FALSE FALSE
# Make a numeric vector and display its structure
numeric.vector <- c(-1:10)
str(numeric.vector)
 int [1:12] -1 0 1 2 3 4 5 6 7 8 ...
# Make a mixed vector and display its structure. Take a note of its typing afterwards
mixed.vector <- c(FALSE, TRUE, 1, 2, "three", 4, 5, ...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
str(mixed.vector)
Error in str(mixed.vector): object 'mixed.vector' not found
# Attempt to coerce our vectors
# logical to numeric
as.numeric(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# numeric to logical
as.logical(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# numeric to character
as.character(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# mixed to a numeric. Note what happens when elements cannot be converted
as.numeric(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context

2.2.3 Data Frames hold tabular data

2.2.3.1 Object classes

Now that we have had the opportunity to create a few different vector objects, let’s talk about what an object class is. An object class can be thought of as a structure with attributes that will behave a certain way when passed to a function. Because of this

  • data frames, lists and matrices have their own classes
  • vectors inherit from their data type (e.g. vectors of characters behave like characters)

Some R package developers have created their own object classes. For example, many of the functions in the tidyverse generate tibble objects. They behave in most ways like a data.frame but have a more refined print structure, making it easier to see information such as column types when viewing them quickly. In general, from a trouble-shooting standpoint, it is good to be aware that your data may need to be formatted to fit a certain class of object when using different packages.

After we are done tidying most of our datasets, they will be in tibble objects, but all of the basic data frame functions apply to these as well.


2.2.3.2 Data frames are groups of vectors aligned as columns

While matrices are 2-dimensional structures limited to a single specific type of data within each instance, data frames treat each column of the structure like a vector. The data frame, however, can have multiple data types mixed across each different column. Data frame rules to remember are:

  1. Within a column, all members must be of the same data type (ie character, numeric, Factor, etc.)
  2. All columns must have the same number of rows (hence the matrix shape)

Data frames allows us to generate tables of mixed information much like an Excel spreadsheet.

# Generate a data frame with different variable/column types
mixed.df <- data.frame(... = character.vector,
                       ... = numeric.vector[2:4],
                       ... = logical.vector[1:3])
Error in data.frame(... = character.vector, ... = numeric.vector[2:4], : object 'character.vector' not found
# View the data frame
mixed.df
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Check the structure of the data frame
str(mixed.df)
Error in str(mixed.df): object 'mixed.df' not found

2.2.3.3 Some useful data frame commands (for now)

  • nrow(data_frame) retrieves the number of rows in a data frame.

  • ncol(data_frame) retrieves the number of columns in a data frame.

  • data_frame$column_name accesses a specific column by it’s name.

  • data_frame[x,y] accesses a specific element located at row x, column y

  • rownames(data_frame) retrieves or assigns row names to your data frame

  • colnames(data_frame) retrieves or assigns columns names to your data frame

There are many more ways to access and manipulate data frames that we’ll explore further down the road. Let’s review some basic data frame code.

# query the dimensions of the data frame
dim(mixed.df)
Error in eval(expr, envir, enclos): object 'mixed.df' not found
nrow(mixed.df)
Error in nrow(mixed.df): object 'mixed.df' not found
ncol(mixed.df)
Error in ncol(mixed.df): object 'mixed.df' not found
# retrieve row and column names
rownames(mixed.df)
Error in rownames(mixed.df): object 'mixed.df' not found
colnames(mixed.df)
Error in is.data.frame(x): object 'mixed.df' not found
# print the mixed data frame
mixed.df
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Access portions of the data frame
# a single column
str(mixed.df$...)
Error in str(mixed.df$...): object 'mixed.df' not found
# a single element
mixed.df[2, 3]
Error in eval(expr, envir, enclos): object 'mixed.df' not found
mixed.df[3, ...]
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# multiple rows
mixed.df[c(1,3), ]
Error in eval(expr, envir, enclos): object 'mixed.df' not found
mixed.df[-2, ]
Error in eval(expr, envir, enclos): object 'mixed.df' not found

2.2.4 Lists are amorphous bundles strung together with code

Lists can hold mixed data types of different lengths. These are especially useful for bundling data of different types to pass around your scripts, and functions, or when receiving output from functions! Rather than having to call multiple variables by name, you can store them in a single list!

If you forget the contents of your list, use the str() function to check out its structure. str() will tell you the number of items in your list and their data types.

# Make a named list of various items
mixed.list <- list(countries = character.vector, values = numeric.vector, mixed.data = ...)
Error in eval(expr, envir, enclos): object 'character.vector' not found
# Look at some information about our list
str(mixed.list)
Error in str(mixed.list): object 'mixed.list' not found
# What are the names of the elements in mixed.list
names(mixed.list)
Error in eval(expr, envir, enclos): object 'mixed.list' not found
# Lists can often be unnamed
unnamed.list <- list(character.vector, numeric.vector, ...)
Error in eval(expr, envir, enclos): object 'character.vector' not found
# Look at some information about our unnamed list
str(unnamed.list)
Error in str(unnamed.list): object 'unnamed.list' not found
names(unnamed.list)
Error in eval(expr, envir, enclos): object 'unnamed.list' not found

2.2.4.1 Accessing elements from a list is accomplished in multiple ways

Accessing lists is much like opening up a box of boxes of chocolates. You never know what you’re gonna get when you forget the structure!

You can access elements with a mixture of number and naming annotations much like data frames. Also [[x]] is meant to access the xth “element” of the list. Note that unnamed lists cannot be accessed with naming annotations.

  • [x] returns a list object with your element(s) of choice in the list.
  • [[x]] returns a “single” element only but that element could be a vector, data frame, list, etc.
# Subset our list with []
mixed.list[c(...)]
Error in eval(expr, envir, enclos): object 'mixed.list' not found
mixed.list[...]
Error in eval(expr, envir, enclos): object 'mixed.list' not found
# Pull out a single element
mixed.list[[2]]
Error in eval(expr, envir, enclos): object 'mixed.list' not found
mixed.list[["countries"]]
Error in eval(expr, envir, enclos): object 'mixed.list' not found
# Give a vector as input to [[]]
mixed.list[[c(1,3)]]
Error in eval(expr, envir, enclos): object 'mixed.list' not found
# vs equivalent
mixed.list[[1]][3]
Error in eval(expr, envir, enclos): object 'mixed.list' not found
# Access a single element from a data frame nested in a list
mixed.list[[c(...)]]
Error in eval(expr, envir, enclos): object 'mixed.list' not found
# vs equivalient
mixed.list[[3]][...]
Error in eval(expr, envir, enclos): object 'mixed.list' not found

Comprehension Question 2.2.4.1: Suppose we had a list named multiDF.list consisting of 3 data frames, as shown in the following code cell. How would you subset the 2nd and 3rd data frames into their own list? How would you access the “values” column from the 3rd data frame? Use the following code cell to help you out.

multiDF.list = list(mixed.df, rbind(mixed.df, mixed.df), rbind(mixed.df, mixed.df, mixed.df))
Error in eval(expr, envir, enclos): object 'mixed.df' not found
str(multiDF.list)
Error in str(multiDF.list): object 'multiDF.list' not found
# Subset the 2nd and 3rd dataframes as their own list

...
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Output the "values" column of the 3rd dataframe

...
Error in eval(expr, envir, enclos): '...' used in an incorrect context

2.3.0 Factors codify your data into categorical variables

Ah, the dreaded factors! A factor is a class of object used to encode a character vector into categories. They are used to store categorical variables and although it is tempting to think of them as character vectors this is a dangerous mistake. Adding or changing data in a data frame with pre-existing factors requires that you match factor levels correctly as well.

Factors make perfect sense if you are a statistician designing a programming language (!) but to everyone else they exist solely to torment us with confusing errors. At its core, a factor is really just an integer vector or character data with an additional attribute, called levels(), which defines the accepted values for that variable.

2.3.0.1 Why use factors?

Why not just use character vectors, you ask?

Believe it or not factors do have some useful properties. For example, factors allow you to specify all possible values a variable may take even if those values are not in your data set. Think of conditional formatting in Excel. We also use them heavily in generating statistical analyses and in grouping data when we want to visualize it.

2.3.0.2 A historical note about R 4.0.x versus r 3.x.x

Since the inception of R, data.frame() calls have been used to create data frames but the default behaviour was to convert strings (and characters) to factors! This is a throwback to the purpose of R, which was to perform statistical analyses on datasets with methods like ANOVA which examine the relationships between variables (ie factors)!

As R has become more popular and its applications and packages have expanded, incoming users have been faced with remembering this obscure behaviour, leading to lost hours of debugging grief as they wonder why they can’t pull information from their dataframes to do a simple analysis on C. elegans strain abundance via molecular inversion probes in datasets of multiplexed populations. #SuspciouslySpecific

That meant that users usually had to create data frames including the toggle

data.frame(name=character(), value=numeric(), stringsAsFactors = FALSE)

Fret no more! As of R 4.x.x the default behaviour has switched and stringsAsFactors = FALSE is the default! Now if we want our characters to be factors, we must convert them explicitly, or turn this behaviour on at the outset of creating each data frame!

# Generate a data frame and include factors
str(data.frame(country = character.vector,
               values = numeric.vector[2:4],
               commonwealth = logical.vector[1:3],
               continent = c("North America", "North America", "Europe"),
               ...)
    )
Error in str(data.frame(country = character.vector, values = numeric.vector[2:4], : '...' used in an incorrect context
# Explicitly define factors for each variable.
str(data.frame(country = ...(character.vector),
               values = numeric.vector[2:4],
               commonwealth = logical.vector[1:3],
               continent = c("North America", "North America", "Europe"),
               stringsAsFactors = FALSE)
    )
Error in ...(character.vector): could not find function "..."

2.3.1 Specify factors and their levels explicitly during or after data.frame creation

From above, you can specify which columns of strings are converted to factors at the time of declaring your column information. Alternatively you can coerce character vectors to factors after generating them.

R’s default behaviour puts factor levels in alphabetical order. This can cause problems if we aren’t aware of it. You can check the order of your factor levels with the levels() command. Furthermore you can specify, during factor creation, your level order.

Always check to make sure your factor levels are what you expect.

With factors, we can deal with our character levels directly, or their numeric equivalents.

# Generate a data frame and include factors
str(data.frame(country = character.vector,
               values = numeric.vector[2:4],
               commonwealth = logical.vector[1:3],
               continent = factor(c("North America", "North America", "Europe"),
                                     ... = c("North America", "Europe"))
              )
   )
Error in data.frame(country = character.vector, values = numeric.vector[2:4], : object 'character.vector' not found
# Coerce a factor
mixed.df <- data.frame(country = character.vector,
                      values = numeric.vector[2:4],
                      commonwealth = logical.vector[1:3],
                      continent = c("North America", "North America", "Europe"))
Error in data.frame(country = character.vector, values = numeric.vector[2:4], : object 'character.vector' not found
# Set our factor after declaring the data frame
mixed.df$continent <- factor(..., levels=c("North America", "Europe"))
Error in eval(expr, envir, enclos): '...' used in an incorrect context
str(mixed.df)
Error in str(mixed.df): object 'mixed.df' not found

2.3.2 More facts about factors

  1. Use levels() to list the levels and their order for your factor

  2. To rename levels of a factor, declare and reassign your factor.

  3. Move a single level to the first position within your factor levels with relevel().

  4. Factor levels can be assigned an order of precedence during their creation with the parameter ordered = TRUE.

  5. Define labels for your factor during their creations with the parameter labels = c(). Note that level order is assigned before labels are added to your data. You are essentially labeling the integer assigned to your factor levels so be careful when using this parameter!

Advanced factors functions with forcats If you’re looking for more advanced functions that you can use to manipulate, sort or update factors, check out the forcats function. With it, you can refactor based on functions, frequency, or explicitly re-specify the order of one or more factor levels. We’ll see this package in action in more detail during later lectures.


2.4.0 Mathematical operations on data frames and arrays

Yes, you can treat data frames and arrays like large lists where mathematical operations can be applied to individual elements or to entire columns or more!

2.4.1 Mathematical operations are applied differently depending on data type

  • numeric data: operations applied as expected
  • non-numeric (ie characters): error will be thrown
  • factors: warning message and NAs returned
  • logical data (TRUE/FALSE): coercion to numeric before applying operations

Therefore be careful to specify your numeric data for mathematical operations.

mixed.df
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Add to each element
mixed.df$values + 3
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Add columns to each other
mixed.df$values + mixed.df$values
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# multiply each element by a constant
mixed.df$values * 4
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# implicit coercion of logical to integer
mixed.df$commonwealth * 5 
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Perform math on a factor
mixed.df$continent * 6
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Convert the factor to a numeric first
as.numeric(mixed.df$continent) * 7
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Can we perform math on non-numeric variables?
...
Error in eval(expr, envir, enclos): '...' used in an incorrect context

2.5.0 Using the apply() family of functions to perform actions across data structures

The above are illustrative examples to see how our different data structures behave. In reality, you will want to do calculations across rows and columns, and not on your entire matrix or data frame.

2.5.1 The apply() function will recognize basic functions and use them on vectorized data

The above are illustrative examples to see how our different data structures behave. In reality, you will want to do calculations across rows and columns, and not on your entire matrix or data frame.

For example, we might have a count table where rows are genes, columns are samples, and we want to know the sum of all the counts for a gene. To do this, we can use the apply() function. apply() Takes an array, matrix (or something that can be coerced as such, like a numeric data frame), and applies a function over rows or columns. The apply() function takes the following parameters:

  • X: an array. matrix or something that can be coerced to these objects
  • MARGIN: defines how to apply the function; 1 = rows, 2 = columns.
  • FUN: the function to be applied. Supplied as a function name without the () suffix
  • ...: this notation means we can pass additional parameters to our function defined by FUN.

and returns a vector, array or list depending on the nature of X.

Let’s practice by invoking the sum function.

# Make a sample data frame of numeric values only
numeric.df = data.frame(geneA = numeric.vector, geneB = numeric.vector*2, geneC = numeric.vector*3)
numeric.df
# Apply sum by rows
apply(numeric.df, ..., sum)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Apply sum by columns
apply(numeric.df, ..., sum)
Error in eval(expr, envir, enclos): '...' used in an incorrect context

2.5.2 The other members of the apply() family

There are 3 additional members of the apply() family that perform similar functions with varying outputs

  1. lapply(data, FUN, ...) is usable on dataframes, lists, and vectors. It returns a list as output.
  • It will coerce non-list objects to a list
  • Additional arguments to FUN will be applied from the ...
  1. sapply(data, FUN, ...) works similarly to lapply() except it tries to simplify the output to the most elementary data structure possible. i.e. it will return the simplest form of the data that makes sense as a representation.

  2. mapply(FUN, data, ...) is short for “multivariate” apply and it applies a function to multiple lists or multiple vector arguments.

# Use lapply on the columns of numeric.df
...(numeric.df, sum)
Error in ...(numeric.df, sum): could not find function "..."
str(lapply(numeric.df, sum))
List of 3
 $ geneA: int 54
 $ geneB: num 108
 $ geneC: num 162
# Use sapply on the columns of numeric.df
...(numeric.df, sum)
Error in ...(numeric.df, sum): could not find function "..."
str(sapply(numeric.df, sum))
 Named num [1:3] 54 108 162
 - attr(*, "names")= chr [1:3] "geneA" "geneB" "geneC"
# Using lapply and sapply and sum on an actual list
sum.list <- list(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
str(sum.list)
List of 2
 $ :'data.frame':   12 obs. of  3 variables:
  ..$ geneA: int [1:12] -1 0 1 2 3 4 5 6 7 8 ...
  ..$ geneB: num [1:12] -2 0 2 4 6 8 10 12 14 16 ...
  ..$ geneC: num [1:12] -3 0 3 6 9 12 15 18 21 24 ...
 $ :'data.frame':   12 obs. of  3 variables:
  ..$ geneA: int [1:12] -1 0 1 2 3 4 5 6 7 8 ...
  ..$ geneB: num [1:12] -2 0 2 4 6 8 10 12 14 16 ...
  ..$ geneC: num [1:12] -3 0 3 6 9 12 15 18 21 24 ...
# lapply on the list returns a list
lapply(sum.list, sum)
[[1]]
[1] 324

[[2]]
[1] 324
# sapply on the list returns a vector
sapply(sum.list, sum)
[1] 324 324
# Use lapply to select portions from a list
sum.list <- list(numeric.df, numeric.df)

# Extract the first row from each member of the list
lapply(sum.list, ...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Extract the 2nd column from each member of the list
lapply(sum.list, "[", , 2)
[[1]]
 [1] -2  0  2  4  6  8 10 12 14 16 18 20

[[2]]
 [1] -2  0  2  4  6  8 10 12 14 16 18 20
# Take a close look at what sapply returns in this case
sapply(sum.list, "[", , 2)
      [,1] [,2]
 [1,]   -2   -2
 [2,]    0    0
 [3,]    2    2
 [4,]    4    4
 [5,]    6    6
 [6,]    8    8
 [7,]   10   10
 [8,]   12   12
 [9,]   14   14
[10,]   16   16
[11,]   18   18
[12,]   20   20

Notice how in using sapply() to extract from a list of data frames, a single matrix was returned - a single output in the simplest form that maintains structure.

Now let’s give mapply() a try.

# Use mapply in an example on numeric.vector
mapply(sum, numeric.vector, numeric.vector)
 [1] -2  0  2  4  6  8 10 12 14 16 18 20
# Use mapply in an example on numeric.df
mapply(sum, numeric.df, numeric.df)
geneA geneB geneC 
  108   216   324 
# Use mapply on the rep function to see its output
mapply(rep, c(...), 4)
Error in mapply(rep, c(...), 4): '...' used in an incorrect context

2.6.0 Special data: NA and NaN values

Missing values in R are handled as NA (Not Available). Impossible values (like the results of dividing by zero) are represented by NaN (Not a Number). These types of values can be considered null values. These two types of values, specially NAs, have special ways to be dealt with, otherwise it may lead to errors in some functions.

For our purposes, we are not interested in keeping NA data within our datasets so we will usually detect and remove them or replace them within our data after it is imported.

2.6.1 Helpful functions and information for dealing with NA data

  1. is.na() returns a logical vector reporting which values from your query are NA.
  2. complete.cases() returns a logical for row without any NA values.
  3. Some functions can ignore NA values with the na.rm = TRUE parameter: ie mean(), sum() etc.
  4. Additional functions in the tidyr package can also be used to work with NA values.
# Add some NAs to our data frame
mixed.df <- data.frame(country = character.vector,
                      values = c(3, ..., 9),
                      commonwealth = logical.vector[1:3],
                      continent = c("North America", "North America", "Europe"),
                      measure = c("metric", NA, "metric")
                      )
Error in data.frame(country = character.vector, values = c(3, ..., 9), : object 'character.vector' not found
# Look at our updated data frame
mixed.df
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Which entries are NA?
is.na(mixed.df)
Error in eval(expr, envir, enclos): object 'mixed.df' not found
# Which rows are incomplete?
complete.cases(mixed.df)
Error in complete.cases(mixed.df): object 'mixed.df' not found
# Use some math functions 
sum(mixed.df$values, ...)
Error in eval(expr, envir, enclos): object 'mixed.df' not found
::: {align=“center”} Each dataset has it’s own problems. Image from: https://cfss.uchicago.edu/notes/tidy-data/ :::

3.0.0 Welcome to the tidyverse

Let’s begin with some definitions: - Variable: A part of an experiment that can be controlled, changed, or measured. - Observation: The results of measuring the variables of interest in an experiment.

In data science, long format is preferred over wide format because it allows for an easier and more efficient subset and manipulation of the data. To read more about wide and long formats, visit here.

Why tidy data?

Data cleaning/wrangling (or dealing with ‘messy’ data) accounts for a huge chunk of a data scientist’s time. Ultimately, we want to get our data into a ‘tidy’ format (long format) where it is easy to manipulate, model and visualize. Having a consistent data structure and tools that work with that standardized data structure can help this process along.

In Tidy data:

  1. Each variable forms a column.
  2. Each observation forms a row.
  3. Every cell is a single value.

This seems pretty straightforward, and it is. It is the datasets you get that will not be straightforward. Having a map of where to take your data is helpful to unraveling its structure and getting it into a usable format.

3.0.1 The 5 most common problems with messy datasets are:

  • common headers are values, not variable names
  • multiple variables are stored in one column
  • variables are stored in both rows and columns
  • a single variable stored in multiple tables
  • multiple types of observational units are stored in the same table

Observational units: Of the three rules, the idea of observational units might be the hardest to grasp. As an example, you may be tracking a puppy population across 4 variables: age, height, weight, fur colour. Each observation unit is a puppy. However, you might be tracking the same puppies across multiple measurements - so a time factor applies. In that case, the observation unit now becomes puppy-time. In that case, each puppy-time measurement belongs in a different table (at least by tidy data standards). This, however, is a simple example and things can get more complex when taking into consideration what defines an observational unit. Check out this blog post by Claus O. Wilke for a little more explanation.

Let’s begin this journey with data import.


3.1.0 Opening and saving files with the readr package - “All roads lead to Rome..”

… but not all roads are easy to travel.

Depending on format, data files can be opened in a number of ways. The simplest methods we will use involve the readr package as part of the tidyverse. These functions have already been developed to simplify the import process for users. The functions we will use most often are:

  • Read in a delimited file: read_delim(), read_csv(), read_tsv(), read_csv2() [European datasets]

  • Read in from a file, line by line: read_lines()

Let’s read in our first dataset so that we can convert from wide to long format.

# Use read_csv to look at our PHU daily case data
covid_phu.df <- read_csv(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Check the structure and characteristics of covid_phu
str(..., give.attr = FALSE)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
head(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
tail(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
any(is.na(covid_phu.df))
Error in eval(expr, envir, enclos): object 'covid_phu.df' not found

3.1.1 Our SARS-CoV-2 public health unit data covers 34 regions

From looking at our data public health unit data, we can see that it begins tracking on 2020-01-23 and goes up until 2023-01-25. That’s over 3 years for anyone keeping track! In total there are observations for 1,099 days across 34 public health units. The final column appears to be a tally running for total cases reported on that date. Although the data no longer represents a semi-accurate accounting of the state of case reporting in Ontario, it will serve (for now) as a good refresher on how to do basic data wrangling.

From the outset, we can see there are some issues with the data set that we’ll want to resolve and we’ll work through some tidyverse functions in order to do that. First let’s quickly review some of the potential problems with our dataset.

  1. There are 34 public health units and a total count for each date. It is preferable for data visualization to collapse all of those public health units into a single variable so that we have a single value new_cases for each Date observation. At the same time we will not collapse Total into that same variable.

  2. The data is rife with NA values. Many instance are likely due to no data being collected on those dates. For our purposes, it may be simpler to replace them with a value of 0.

  3. Our public health unit names are a little clunky. We should trim them down to simpler region names.

In the end, we want to convert our data to look something like this:

date <date> total_phu_new <dbl> public_health_unit <fct> new_cases <dbl>
2020-01-23 0 Algoma 0
2020-01-23 0 Brant_County 0
2020-01-23 0 Chatham_Kent 0

Before we tackle these issues, let’s go ahead and review some of the tools at our disposal.


3.2.0 The tidyverse package and it’s contents make manipulating data easier

While the tidyverse is composed of multiple packages, we will be focused on working with a subset of these: dplyr, tidyr, and stringr.

3.2.0.1 Redirect your output with %>% whenever you can!

To save on making extra variables in memory and to help make our code more concise, we should use of the %>% symbol. This is a redirection or pipe symbol similar to the | in Unix operating systems and is used for redirecting output from one function to the input of another. By thoughtfully combining this with other commands, we can alter or query our datasets with ease.

We’ll also introduce the %<>% in this class. This is a little more advanced but it allows us to assign the final product of our chain of commands to the very first object.

Whenever we are redirecting, we are implicitly passing our output to the first parameter of the next function. We may not always want to use the entirety of the output or we may want to also reuse that redirected output as part of another parameter. To do so we can use . to explicitly denote the redirected output.

3.2.0.2 dplyr has functions for accessing and altering your data

We will use the “verbs” of the dplyr function often to massage the look of our data by changing column names or subsetting it. The most common verbs you will see in this course are.

Function(s) Description
arrange() Arranging rows by column values
count(), tally() Counting observations by group
distinct() Subsetting rows by distinct or unique values
filter() Subsetting rows by column values
mutate(), transmute() Create, modify, or delete columns
select() Subset columns using their names and types
summarize() or summarise() Summarize by groups to fewer rows
group_by() vs. ungroup() group by one or more variables
rowwise() group data as single rows for calculations across each
rename(), and relocate() Rename or move columns

3.2.0.3 tidyr has additional functions for reshaping our data

The tidyr package will be most useful when we are trying to reshape our data from the wide to the long format or vice versa. This is much more useful for when we want to drastically alter portions or all of our data.

Function(s) Description
pivot_longer() Pivot data from wide to long
pivot_wider() Pivot data from long to wide
extract() Extract a character column into multiple groups
separate() Separate a character column into multiple groups
unite() Unite multiple columns into one by pasting strings
drop_na() Drop rows containing missing values
replace_na() Replace NAs with specific values

3.2.0.4 stringr provides functionality for searching data based on regular expressions

The stringr package will come in most useful when we are trying to fix string issues with our data. Many time our headers or data will contain spaces or poor formatting. Many times we will prefer to have our headers in lower case format, with any spaces replaced by an _. We’ll also use verbs from this package to make any variables or data more concise.

Category Function(s) Description
String analysis str_count() Count the number of matches in a string
String retrieval str_detect() Detect the presence (or absence) of a pattern in string
str_extract() and str_extract_all() Extract matching patterns from a string
str_match() and str_match_all() Extract matched groups from a string
str_subset() and str_which() Keep or find strings matching a pattern
String alteration str_remove() and str_remove_all() Remove matched patterns from a string
str_split(), str_split_fixed(), and str_split_n() Split a string into pieces
str_c() Concatenate multiple strings into a single string with optional separator
str_flatten() Flatten a string
str_sub() Extract and replace substrings from a character vector
str_to_upper() and str_to_lower() Convert case of a string

Time to tackle our dataset!

3.2.1 Reformat our wide table with pivot_longer()

As you may recall, our PHU data is formatted such that each column represents new cases per day for a single PHU. It’s a great way to format for data entry and certainly reduces on redundancy. However, for us to work with this data, we want to collapse all of those PHUs into a single column.

Today we will use the pivot_longer() function to convert our wide-format data over to long-format. For our purposes, we will rely on four parameters: 1. data: the data frame (and columns) that we wish to transform. 2. cols: the columns that we wish to gather/collapse into a long format. 3. names_to: the variable name of the new column to hold the collapsed information from our current columns. 4. values_to: The variable name of the values for each observation that we are collapsing down.

We’ll be using a series of %>% so for now we won’t save our work to a new object.

# A reminder of what our data looks like
covid_phu.df %>% head()
Error in head(.): object 'covid_phu.df' not found
# Start with our wide-format phu data
covid_phu.df %>% 

# Pivot the data into a long-format set
pivot_longer(cols = ..., names_to = ..., values_to = ...) %>% 

# Just take a quick look at the output.
str()
Error in str(.): '...' used in an incorrect context

3.2.2 Replace NA values from our data with replace_na()

Our conversion to long format creates 37,366 observations relating a Date to a new_cases value in a specific Public_Health_Unit (or total). From the looks of our data, however, we do have NA values under our new_cases variable.

We have two options: 1. Remove the NA observations from our data set. There won’t be any loss of information since we could rebuild the original data if we really needed to. 2. Replace the NA observations with a value that makes sense for our analysis.

Let’s replace the missing observations with a new value, 0, using replace_na(). This function will need two parameters:

  1. data: the data frame or vector that it will scan for NA values.
  2. replace: the value that we will use to replace NA.

We’re going to update our pipe of commands and save the final output into a new variable covid_phu_long.df.

# Pivot the data into a long-format set and remove NAs from the value table
covid_phu_long.df <- 
    covid_phu.df %>% 
    pivot_longer(cols = c(2:35), names_to = "public_health_unit", values_to = "new_cases")  %>%

    ### Change the values of "new_cases" using the mutate function
    mutate(new_cases = ...)
Error in covid_phu.df %>% pivot_longer(cols = c(2:35), names_to = "public_health_unit", : '...' used in an incorrect context
# Check that we have covered all of the NA values in our data frame by looking for complete cases
nrow(covid_phu_long.df[complete.cases(covid_phu_long.df),])
Error in nrow(covid_phu_long.df[complete.cases(covid_phu_long.df), ]): object 'covid_phu_long.df' not found
# Or just check for NA values
any(is.na(covid_phu_long.df))
Error in eval(expr, envir, enclos): object 'covid_phu_long.df' not found
# Take a look at the Public Health Unit names
print(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context

3.2.3 Reformat our public health unit names with str_replace_all()

Looking at our PHU names, we can see that there is a lot of redundancy in our names. We see they may sometimes end or begin in some form of: - _District - _Region - _City - County - City_of

We have a couple of choices but we can either use str_replace_all() or a specific version of that, str_remove_all(), which simply replaces a pattern with an empty character.

For str_replace_all() we will supply: 1. string: a single string or vector of strings. 2. pattern: the pattern we wish to search for in the form of a string or regular expression. 3. replace: the replacement string we wish to use.

For the purposes of our visualization and now that these are now longer column names, we will replace all remaining underscore (_) characters with a space. To wrap that up we’ll convert our updated variable to a factor and overwrite our original covid_phu_long.df.

We will accomplish this all through multiple calls to mutate.

# Clean up the Public Health Unit names
covid_phu_long.df  %<>% 

# Replaces our public_health_unit values with ones where we remove excess verbage
mutate(public_health_unit = str_replace_all(string = ..., 
                                            pattern = ...,
                                            replace = "")) %>% 

# From the updated version of public_health_unit, replace all _ with a " "
mutate(public_health_unit = str_replace_all(string = ..., 
                                            pattern = "_",
                                            replace = " ")) %>% 

# Now make sure that it's a factor for later
mutate(public_health_unit = as.factor(public_health_unit))
Error in mutate(., public_health_unit = str_replace_all(string = ..., : object 'covid_phu_long.df' not found
# Take a look at the new set of phu names
print(levels(covid_phu_long.df$public_health_unit))
Error in levels(covid_phu_long.df$public_health_unit): object 'covid_phu_long.df' not found
# Take a quick look at our final dataset
head(covid_phu_long.df)
Error in head(covid_phu_long.df): object 'covid_phu_long.df' not found
# Make a quick copy here too
covid_phu_long_copy.df = covid_phu_long.df
Error in eval(expr, envir, enclos): object 'covid_phu_long.df' not found

3.2.4 rename() variables for clarity

Now that we have the basic structure for our data, we want to clean it up just a little bit by renaming our Total column to clarify that it represents total new cases across all PHUs for that date. Why did we keep this column separate? Now we can use this information to generate percentage totals for each PHU if we choose to. We’ll also change our Date column to lower case at the same time.

We’ll use rename() from dplyr to accomplish the task of renaming our column. There are a number of ways you could accomplish this without using dplyr but the simplicity of it is nice.

# Rename our Total column to clarify it's meaning
covid_phu_long.df  %>% 
rename(... = Total,
       ... = Date) %>% 
head()
Error in rename(., ... = Total, ... = Date): object 'covid_phu_long.df' not found

3.2.5 Reorder your columns with relocate()

The last cleanup we can accomplish with our data is to move total_phu_new to the last column of our data frame. This is for personal preference but also makes more sense when simply looking at the data. The relocate() verb from dplyr accomplishes this with ease since we are not dropping or removing columns. It uses some extra syntax to help accomplish its functions:

  1. .data: the data frame or tibble we want to alter
  2. ...: the columns we wish to move
  3. .before or .after: determines the destination of the columns. Supplying neither will move columns to the left-hand side.

In fact, relocate() can be used to rename a column as well but it will also be moved by default so consider the ramifications of such an action!

Note: We could accomplish a similar result using the select command as well. It’s really up to what you’re comfortable with but it is much simpler to use relocate() when you are working with a large number of columns and you want to move one to a specific location.

# Rename our Total column to clarify it's meaning
covid_phu_long.df  %<>% 
rename(total_phu_new = Total,
       date = Date) %>% 
# relocate our total column to the right side
relocate(total_phu_new, ... = new_cases)
Error in rename(., total_phu_new = Total, date = Date): object 'covid_phu_long.df' not found
head(covid_phu_long.df)
Error in head(covid_phu_long.df): object 'covid_phu_long.df' not found

Comprehension Question 3.2.5: In the above example we used the relocate() function to move the “total_phu_new” column to the end of our data frame. What other methods could we use to accomplish the same feat? Use the below code cell to help yourself out.

# Relocate our target column using the select() command
covid_phu_long_copy.df  %>% 
rename(total_phu_new = Total,
       date = Date) %>% 
# relocate our total column to the right side
... %>% 

head()
Error in ...(.): could not find function "..."

3.3.0 Save your data to a file - “Country roads… save to home!”

At this point we have completed the data wrangling we want to accomplish on this dataset. We’ve converted it to a long-format and renamed the PHU entries while removing any NA values that may cause issues. There are a number of ways we could save this data now either as a text file or in its current form as a data frame in a .RData format.

  • Write out to a delimited file: write_delim(), write_csv(), write_tsv(), write_excel_csv()
  • Write out to a file, line by line: write_lines()
  • Save an object to a .Rdata file: save()
  • Load an object from a .Rdata file: load()

Let’s try some of those methods now.

# Check the files names we currently have
print(dir("./data/"))
[1] "hospitalizations_pt.csv"                        
[2] "Lecture01.RData"                                
[3] "Ontario_covidtesting.csv"                       
[4] "Ontario_daily_change_in_cases_by_phu.csv"       
[5] "Ontario_daily_change_in_cases_by_phu_long.RData"
[6] "Ontario_daily_change_in_cases_by_phu_long.tsv"  
[7] "Ontario_phu_data.all.facet.png"                 
[8] "region_hospital_icu_covid_data.csv"             
# Write covid_phu_long.df to a tab-delimited file
...(covid_phu_long.df, file = "./data/Ontario_daily_change_in_cases_by_phu_long.tsv")
Error in ...(covid_phu_long.df, file = "./data/Ontario_daily_change_in_cases_by_phu_long.tsv"): could not find function "..."
# Check our file names after writing
print(dir("./data/"))
[1] "hospitalizations_pt.csv"                        
[2] "Lecture01.RData"                                
[3] "Ontario_covidtesting.csv"                       
[4] "Ontario_daily_change_in_cases_by_phu.csv"       
[5] "Ontario_daily_change_in_cases_by_phu_long.RData"
[6] "Ontario_daily_change_in_cases_by_phu_long.tsv"  
[7] "Ontario_phu_data.all.facet.png"                 
[8] "region_hospital_icu_covid_data.csv"             
# Save our data frame as an object
save(covid_phu_long.df, file="./data/Ontario_daily_change_in_cases_by_phu_long.RData")
Error in save(covid_phu_long.df, file = "./data/Ontario_daily_change_in_cases_by_phu_long.RData"): object 'covid_phu_long.df' not found
# Check our file names after saving
print(dir("./data/"))
[1] "hospitalizations_pt.csv"                        
[2] "Lecture01.RData"                                
[3] "Ontario_covidtesting.csv"                       
[4] "Ontario_daily_change_in_cases_by_phu.csv"       
[5] "Ontario_daily_change_in_cases_by_phu_long.RData"
[6] "Ontario_daily_change_in_cases_by_phu_long.tsv"  
[7] "Ontario_phu_data.all.facet.png"                 
[8] "region_hospital_icu_covid_data.csv"             

3.3.0.1 readxl and writexl packages for working with excel spreadsheets

Not all of your data may come as a comma- or tab-delimited format. In the case of excel spreadsheets there are some packages available that can also facilitate the parsing of these more complex files. The readxl package is part of the tidyverse but writexl package is not. There are other means of writing to an excel file format but they are dependent on other programs (like Java or Excel) or their drivers.

From the readxl package

  • Get a list of sheet names from a file: excel_sheets()
  • Read in an excel sheet: read_excel()

From the writexl package (not a part of the tidyverse) but independent of Java and Excel

  • Write out to xlsx format: write_xlsx()
  • Can write a list of objects to separate sheets but cannot append to pre-existing files.

4.0.0 Simple graphical analysis of data with ggplot2

We now have some data in a tidy format that we’d like to visualize. We can begin with some initial analyses of the data using the ggplot2 package. It has all of the components we need to help us decide on which data we want to focus on or keep. There are a number of ways to visualize our data and here we will refresh our ggplot skills.

Basic ggplot notes: - ggplot objects hold a complex number of attributes but always need an initial source of data

As we start to produce plot figures, they’ll vary in size depending on your needs. In an R Markdown code cell, you can set your figure size using the code cell attributes much like the parameters of a function. You can set the figure size dimensions using fig.width and fig.height. As we proceed in the future, you’ll see us setting these attributes within our code cells.

# Initialize a plot with our data
phu.plot <- ggplot(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Take a quick look at the structure of the data
str(phu.plot)
Error in str(phu.plot): object 'phu.plot' not found

4.1.0 Make a line graph of new cases based on each PHU across all dates

We now have a basic plot object initialized but we need to tell it how to display the data associated with it. We’ll begin with a simple line graph of all the public health units across all dates within the set.

In order to update or add layers to a ggplot object, we can use the + symbol for each command. For instance, to define the source of x-axis and y-axis data, we use aes() command to update the aesthetics layer. Remember how we defined the public_health_unit variable as a factor? We’ll take advantage of that here and tell ggplot to give each PHU it’s own colour.

After defining our aesthetics, we still need to tell ggplot how to actually graph the data. The ggplot package comes with an abundance of visualizations accessed through the geom_*() commands. Some examples include

  • geom_point() for scatterplots
  • geom_line() for line graphs
  • geom_boxplot() for boxplots
  • geom_violin() for violin plots
  • geom_bar() for bargraphs
  • geom_histogram() for histograms
# Update the aesthetics with axis and colour information, then add a line graph!
phu.plot +
    # 2. Aesthetics
    aes(x = ..., y = ..., colour = ...) +
    theme(text = element_text(size = 20)) + # set text size
    guides(colour = guide_legend(title="Public Health Unit")) + # Legend title
    xlab("Date") + # Set the x-axis label
    ylab("New cases") + # Set the y-axis label

    # 4. Geoms
    geom_line()
Error in eval(expr, envir, enclos): object 'phu.plot' not found

4.2.0 Use the facet_wrap() command to break PHUs into separate graphs

There’s a lot of data on that graph and some of it is quite drowned out because of the scale of PHUs with many more cases. To break out each PHU individually, we can add the facet_wrap() command. We’ll also update some of the parameters:

  • scale: we will update this so each y-axis scale is determined by PHU-specific data.
  • ncol: use this to set the number of columns displayed in our grid

At the same time, we’ll also get rid of the legend since each individual graph will be labeled by its PHU.

# This is going to be a big graph so adjust our plot window sizes for us
options(repr.plot.width=20, repr.plot.height=30)

# Add a facet_wrap and get rid of the legend
phu_facet.plot <- phu.plot +
    # 2. Aesthetics
    aes(x = date, y = new_cases, colour = public_health_unit) +

    theme(text = element_text(size = 20)) + # set text size
        
    # Give titles to your axes
    xlab("Date") + # Set the x-axis label
    ylab("New cases") + # Set the y-axis label
    ggtitle("New cases per day across Ontario Public Health Units") +

    # Remove the legend
    theme(legend.position = "none") +

    # 4. Geoms
    geom_line() +

    # 7. ### 4.2.0 Facet our data by PHU
    facet_wrap(~ ..., scales = ..., ncol=...)
Error in eval(expr, envir, enclos): object 'phu.plot' not found
# Display our plot
phu_facet.plot
Error in eval(expr, envir, enclos): object 'phu_facet.plot' not found

4.3.0 Use the ggsave() command to save your plots to a file

There are a number of ways you can use the ggsave() command to specify how you want to save your files.

# What is our working directory?
getwd()
[1] "C:/Users/mokca/Dropbox/!CAGEF/Course_Materials/Advanced_Graphics_in_R/2024.03_Adv_Graphics_R/Lecture_01_R_Introduction"
C:/Users/mokca/Dropbox/!CAGEF/Course_Materials/Advanced_Graphics_in_R/2024.03_Adv_Graphics_R/Lecture_01_R_Introduction
# Save the plot we've generated to the root directory of the lecture files.
ggsave(..., 
       filename = "data/Ontario_phu_data.all.facet.png", 
       scale=2, 
       device = "png", 
       units = c("cm"), width = 20, height = 30)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Take a look at the directory
dir("data/")
[1] "hospitalizations_pt.csv"                        
[2] "Lecture01.RData"                                
[3] "Ontario_covidtesting.csv"                       
[4] "Ontario_daily_change_in_cases_by_phu.csv"       
[5] "Ontario_daily_change_in_cases_by_phu_long.RData"
[6] "Ontario_daily_change_in_cases_by_phu_long.tsv"  
[7] "Ontario_phu_data.all.facet.png"                 
[8] "region_hospital_icu_covid_data.csv"             
hospitalizations_pt.csv

Lecture01.RData

Ontario_covidtesting.csv

Ontario_daily_change_in_cases_by_phu.csv

Ontario_daily_change_in_cases_by_phu_long.RData

Ontario_daily_change_in_cases_by_phu_long.tsv

Ontario_phu_data.all.facet.png

region_hospital_icu_covid_data.csv

4.4.0 Barplots can be used to summarize your data across PHUs

Although we do have a running total for each date, what if we want to look at the totals cases across subsets of the PHUs? Using a barplot we can stack cases by date and get a sense of daily case totals from which sets of PHUs we desire.

This time we will use geom_bar() to display our data and tell it to use the values from our new_cases variable to generate the totals. We do this by setting the stat = "identity" parameter.

At the same time, let’s update our colours to use a colour-blind friendly palette scheme.

phu.plot +
    # 2. Aesthetics
    aes(x = date, y= new_cases, fill = ...) + # set our fill colour instead of line colour

    theme(text = element_text(size = 20)) + # set text size
    guides(fill = guide_legend(title="Public Health Unit")) +

    # Give titles to your axes
    xlab("Date") + # Set the x-axis label
    ylab("New cases") + # Set the y-axis label
    ggtitle("New cases per day across all Ontario Public Health Units") +

    # Set up our barplot here
    geom_bar(...) + 
    scale_fill_viridis_d() # the "d" stands for discrete colour scale
Error in eval(expr, envir, enclos): object 'phu.plot' not found

4.4.1 Alter your bin widths to monthly totals by transforming your x-axis

From above we get a sense of overall totals for some PHU distributions but it’s still too much to look at. Let’s transform our x-axis values so we can bin by months instead. To accomplish this we’ll use the as.yearmon() function found in the zoo package we loaded at the beginning of the lecture.

phu.plot +
    # 2. Aesthetics
    aes(x = ..., ### 4.4.1 Update the x-axis by transforming the values to year-month format
        y= new_cases, 
        fill = public_health_unit) + # set our fill colour instead of line colour

    theme(text = element_text(size = 20)) + # set text size
    guides(fill = guide_legend(title="Public Health Unit")) +

    # Give titles to your axes
    xlab("Date") + # Set the x-axis label
    ylab("New cases") + # Set the y-axis label
    ggtitle("New cases per month across all Ontario Public Health Units") +

    # Set up our barplot here
    geom_bar(stat = "identity") + 
    scale_fill_viridis_d() # the "d" stands for discrete colour scale
Error in eval(expr, envir, enclos): object 'phu.plot' not found

4.5.0 Filter your data for what you want to display

Now that we have taken an initial look at our data, we can see that even after converting our axis to a month-year format, it appears that some of the data isn’t that relevant for us. Some of the PHUs are not generating many new cases per day so we can now consider slicing our data up to look at specific regions.

Let’s look at the top 10 regions by total caseload across the dataset.

# What are the top 10 regions by total caseload?
covid_phu_long.df %>% 

# group the data by public health unit
group_by(...) %>% 

# Summarize it by the total number of new cases in each PHU
summarise(...) %>% 

# Sort all of the data in descending order by total cases
arrange(...) %>% 

# take the top 10 PHUs
.[1:10, ]
Error in covid_phu_long.df %>% group_by(...) %>% summarise(...) %>% arrange(...) %>% : '...' used in an incorrect context
# Generate a list of all PHUs and sort by total caseload
# Generate a list of all PHUs and sort by total caseload
phu_by_total_cases_desc <- covid_phu_long.df %>% 

# Group by public health unit
group_by(public_health_unit) %>% 

# Based on public health unit, sum the total cases
summarise(total_cases = sum(new_cases)) %>% 

# Sort by descending order
arrange(desc(total_cases)) %>% 

# Grab the PHU names and convert them into a character vector
select(...) %>% 
unlist() %>% 
as.character() # Coercion to a vector removes the names. unname() works as well.
Error in unlist(.): '...' used in an incorrect context
# Take a look at the public health units
print(phu_by_total_cases_desc)
Error in print(phu_by_total_cases_desc): object 'phu_by_total_cases_desc' not found

4.5.1 Use the filter() command to make a subset of our data

Now that we have a list of PHUs ordered by descending total cases, we can use that to filter our covid_phu_long.df dataframe and graph only the more heavily infected PHUs. We can then pipe the filtered data over to make a ggplot() object. At the same time we’ll do a few more things:

  1. Reorder our factors so that the bars and legend display the PHUs in ascending order by new cases.
  2. Alter the plot title to reflect the data we are using.
# Make a bar graph
covid_phu_long.df %>% 

### 4.5.1 Filter our data based on the PHUs we want to see
filter(...) %>% 

# Redirect our new data frame to ggplot
ggplot(.) +
    # 2. Aesthetics
    aes(x = as.yearmon(date), 
        y= new_cases, 
        fill = fct_reorder(public_health_unit, new_cases)) + # reordering the levels of the data supplied

    theme(text = element_text(size = 20)) + # set text size
    guides(fill = guide_legend(title="Public Health Unit")) +

    # Give titles to your axes
    xlab("Date") + # Set the x-axis label
    ylab("New cases") + # Set the y-axis label
    ggtitle("New cases per month across top 3 Ontario Public Health Units") +

    # Set up our barplot here
    geom_bar(stat = "identity") + 
    scale_fill_viridis_d() # the "d" stands for discrete colour scale
Error in ggplot(.): '...' used in an incorrect context

4.6.0 Looking at the effect of lockdown on new cases

We can see from our first graph of daily case loads that there can be quite a bit of variability from day to day. Rather than look at the daily tally of new cases, perhaps we can take into account the overall number of new cases appearing in a 14-day sliding window. Given that symptoms from time of infection can take between 5-14 days to manifest, then a portion of daily positive cases can be the result of infection going back as far as 14-days. Taking a look at a 14-day mean average will also smooth out our data as we see below:

To accomplish the above visualization, we’ll need to perform some transformations on our dataset.

  1. Ensure our data is grouped by public health unit
  2. Summarise our data in sliding windows of 14-day length

We’ll want to track observations by: - public health unit - cases in the window - window start date - window end date

# Shut down some output information from the summarise function
options(dplyr.summarise.inform = FALSE)

# 1. group our data by public health unit
covid_phu_long.df <- covid_phu_long.df %>% group_by(public_health_unit)
Error in group_by(., public_health_unit): object 'covid_phu_long.df' not found
# 2. get a complete list of case dates
case.dates <- unique(covid_phu_long.df$date)
Error in unique(covid_phu_long.df$date): object 'covid_phu_long.df' not found
# 3. set up a table to hold our summarised results
phu_window_data.df = data.frame(public_health_unit = character(0),
                               window_mean = numeric(0),
                               start_date = numeric(0), end_date = numeric(0))

case_window = 14-1

# Iterate through the dates in a 14-day sliding window
for (i in 1:(length(case.dates)-case_window)) {
    
    curr.set <- covid_phu_long.df %>%
                # Filter for a set of data that spans 14 days
                filter(date %in% case.dates[i:(i + case_window)]) %>% 
                # Summarize that data based on public health unit
                summarize(window_mean = mean(new_cases))
    
    # Track the start and end dates of the window
    curr.set$start_date = case.dates[...]
    curr.set$end_date = case.dates[...]
    
    # Add this table to the collected data
    phu_window_data.df <- ...
}
Error in eval(expr, envir, enclos): object 'case.dates' not found
# Check on the final structure of the data
str(phu_window_data.df)
'data.frame':   0 obs. of  4 variables:
 $ public_health_unit: chr 
 $ window_mean       : num 
 $ start_date        : num 
 $ end_date          : num 

4.6.1 Plot our windowed data as a line graph

Now that we’ve generated our windowed data, let’s plot the top 5 PHUs by caseload. Let’s also annotate some dates from the our pandemic history:

  • March 23rd, 2020: Toronto declares state of emergency
  • March 24th, 2020: Our PHU data collections begins
  • March 31st, 2020: Toronto cancels all city-led major events
  • May 23rd, 2020: Trinity Bellwoods Park incident
  • July 31st, 2020: Toronto is allowed to enter Stage 3 reopening
  • September 15th, 2020: Return to public school for TDSB
  • October 10th, 2020: 4-week rollback to modified stage 2
  • November 14th, 2020: Toronto further restricted to Control/red tier.
  • November 23rd, 2020: All non-essential services are ordered closed
  • December 26th, 2020: Second province-wide shutdown issued
  • February 16th, 2021: Students return to in-person classes
  • March 5th, 2021: Toronto exits stay-at-home orders and enters lockdown/grey zone.
  • April 3rd, 2021: Third province-wide shutdown issued
  • November 28th, 2021: First two cases of Omicron variant reported in Ontario
  • December 31st, 2021: Ontario limits PCR testing

4.6.2 Here’s what we’ll do:

  1. Plot the windowed data filtered by the top 5 PHUs
  2. Clean up the graph a little bit by “simplifying” the themes
  3. Annotate 4 dates from the pandemic timeline

4.6.3 Additional layers we’ll see:

  1. theme(): we can use this layer to access any number of elements regarding the overall look/feel of our visualization
  2. scale_*: the scale layers allow us to alter the parameters of how our axis values are calculated or even colours of various components!
  3. geom_text(): used to directly add text based on a mix of variables pulled from your data or specific start/end points
  4. annotate(): a layer to overlay components onto your visualization like shapes, or arrows etc.

In the coming weeks we’ll be digging into the meaning of these more but for this week, it’s a bit of a trial by fire/memory.

# Build our plot and save to an object
phu_window.plot <- phu_window_data.df %>% 
# Filter for the top 5 infected PHUs
filter(public_health_unit %in% phu_by_total_cases_desc[1:5]) %>% 

# redirect the filtered result to ggplot
ggplot(.) +
    # 2. Aesthetics
    aes(x = ..., y = ..., colour = fct_reorder(public_health_unit, ..., .desc=TRUE)) +

    theme_bw() + # Simplify the theme
    xlab("Date") +
    ylab("Mean cases in 14-day window") +
    ggtitle("Mean cases in a 14-day window across top 5 Ontario Public Health Units") +

    theme(text = element_text(size = 20)) + # set text size
    guides(colour = guide_legend(title="Public Health Unit")) + # set our legend name
    theme(panel.grid.major.y = element_line(color="grey95")) + # darken our major y grid
    theme(panel.grid.minor.y = element_blank()) + # remove our minor y grid
    theme(panel.grid.minor.x = element_blank()) + # remove our minor x grid
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) + # rotate our x-axis text

    # 3. Scaling
    # Start looking at data from March 2020 onwards
    scale_x_date(limits = c(...),
                 date_breaks = ..., date_labels = ...) +

    scale_color_viridis_d() + 

    # 4. Geoms
    geom_line(linewidth=1.5) + # Note that "size=1.5" works here too but is deprecated

    # Winter 2020 lockdown
    geom_text(aes(x=as.Date("2020-12-26") + 7, label = "Province-wide lockdown", y=2200), 
              angle=90, size=10, colour="black") +
    annotate("rect", xmin=as.Date("2020-12-26"), xmax=as.Date("2020-12-26") + 14, 
             ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +

    # Spring 2021 Lockdown
    geom_text(aes(x=as.Date("2021-04-03") + 7, label = "Province-wide lockdown", y=2200), 
              angle=90, size=10, colour="black") +
    annotate("rect", xmin=as.Date("2021-04-03"), xmax=as.Date("2021-04-03") + 14, 
             ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +

    # Omicron arrives
    geom_text(aes(x=as.Date("2021-11-30"), label = "First Omicron\ncases reported\nin Ontario", y=1000), 
              hjust=1, vjust = 0, size=10, colour="black") +
    annotate("segment", x=as.Date("2021-11-28"), xend = as.Date("2021-11-28"),
             y=800, yend=100, colour="red", linewidth = 2, arrow = arrow()) +

    # Ontario ends proper PCR testing
    geom_text(aes(x=as.Date("2022-03-01"), label = "Ontario reduces public\nPCR COVID-19 testing", y=2500), 
              hjust=0, size=10, colour="black") +
    annotate("segment", x=as.Date("2022-03-01"), xend = as.Date("2021-12-31"),
             y=2500, yend=2500, colour="red", linewidth = 2, arrow = arrow()) 
Error in `filter()`:
i In argument: `public_health_unit %in% phu_by_total_cases_desc[1:5]`.
Caused by error in `public_health_unit %in% phu_by_total_cases_desc[1:5]`:
! object 'phu_by_total_cases_desc' not found
# plot our object to standard output
phu_window.plot
Error in eval(expr, envir, enclos): object 'phu_window.plot' not found
# If you wanted to save your plot:
# ggsave(phu_window.plot, file="images/top5_PHU_cases_14d-window.png", scale=1, device = "png", units = c("in"), width=20, height=10)

4.7.0 Don’t forget the hospitalization data!

One of the last things we want to cover before wrapping up is the importance of grouping your data and summarizing it. This paradigm is often a simple and powerful way to generate summary information about your various data groups/experiments.

Looking back at our last data series, it was noted that after December 2022, the metrics concerning new case counts became unreliable due to a reduction in COVID-19 testing of the public. Instead, due to the influx of cases, it became more accurate to monitor metrics like hospitalizations and COVID waste water signal.

To this end, let’s look at the COVID hospitalization data by importing region_hospital_ic_covid_data.csv from our data folder. This gives us an idea of the stress being applied to the healthcare system and can also give us an idea of how severe the pandemic may be from wave to wave.

# Import the hospitalization data
covid_hospitalizations <- read_csv(...)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Take a quick look
str(covid_hospitalizations)
Error in str(covid_hospitalizations): object 'covid_hospitalizations' not found
# How many regions are there?
unique(covid_hospitalizations$oh_region)
Error in unique(covid_hospitalizations$oh_region): object 'covid_hospitalizations' not found

4.7.1 Use the group_by() and summarize() paradigm to analyse data

So it looks like our hospitalization data begins around April 2020 and includes multiple metrics involving the status of ICU patients bu also the number of current hospitalizations. There is also a variable oh_region which should denote the health region from which the data is sampled.

The 5 regions reported can vary in size and resources but we can combine these regions into single values to look at the overall number of hospitalizations on a daily basis. To accomplish this feat we’ll turn to the group_by() and summarize() functions.

The key to using these is to identify the goals of your analysis. In the current case, we want to combine all 5 health regions into a singular one based on the date variable. From there we wish to calculate the sum() of each group on a variable like hospitalizations.

# Pass the hospitalization data 
covid_hospitalizations %>% 
    # Group the data by DATE
    group_by(...) %>% 
    # Summarize each group as a sum of "hospitalizations"
    summarize(...) %>% 

    # Take a look at the data
    head()
Error in head(.): '...' used in an incorrect context

4.7.2 Plot our data using the same code

Now that we know how to summarize the data, we can work on visualing it. For the purposes of comparison, we can reuse our code from before and simply substitute in the new parameters for our visualizations (ie x and y values).

# Pass the hospitalization data 
covid_hospitalizations %>% 
    # Group the data by DATE
    group_by(date) %>% 
    # Summarize each group as a sum of "hospitalizations"
    summarize(all_curr_hospitalizations = sum(hospitalizations)) %>% 

# redirect the filtered result to ggplot
ggplot(.) +
    ### 2. Aesthetics: update the x and y sources
    aes(x = ..., y = ...) +

    theme_bw() + # Simplify the theme
    xlab("Date") +
    ylab("COVID hospitalizations") +
    ggtitle("Current hospitalizations per day across Ontario") +

    theme(text = element_text(size = 20)) + # set text size
    theme(panel.grid.major.y = element_line(color="grey95")) + # darken our major y grid
    theme(panel.grid.minor.y = element_blank()) + # remove our minor y grid
    theme(panel.grid.minor.x = element_blank()) + # remove our minor x grid
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) + # rotate our x-axis text

    # 3. Scaling
    # Start looking at data from March 2020 onwards
    scale_x_date(limits = c(as.Date("2020-03-01"), as.Date(max(phu_window_data.df$start_date))),
                 date_breaks = "1 month", date_labels = "%b-%Y") +

    scale_color_viridis_d() + 

    # 4. Geoms
    geom_line(linewidth=1.5) +

    # Winter 2020 lockdown
    geom_text(aes(x=as.Date("2020-12-26") + 7, label = "Province-wide lockdown", y=1800), 
              angle=90, hjust = 0, size=10, colour="black") +
    annotate("rect", xmin=as.Date("2020-12-26"), xmax=as.Date("2020-12-26") + 14, 
             ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +

    # Spring 2021 Lockdown
    geom_text(aes(x=as.Date("2021-04-03") + 7, label = "Province-wide lockdown", y=1800), 
              angle=90, hjust = 0, size=10, colour="black") +
    annotate("rect", xmin=as.Date("2021-04-03"), xmax=as.Date("2021-04-03") + 14, 
             ymin=-Inf, ymax=Inf, fill="red", alpha=0.2) +

    # Omicron arrives
    geom_text(aes(x=as.Date("2021-11-30"), label = "First Omicron\ncases reported\nin Ontario", y=1500), 
              hjust=1, vjust = 0, size=10, colour="black") +
    annotate("segment", x=as.Date("2021-11-28"), xend = as.Date("2021-11-28"),
             y=1200, yend=500, colour="red", linewidth = 2, arrow = arrow()) +

    # Ontario ends proper PCR testing
    geom_text(aes(x=as.Date("2022-03-01"), label = "Ontario reduces public\nPCR COVID-19 testing", y=2500), 
              hjust=0, size=10, colour="black") +
    annotate("segment", x=as.Date("2022-03-01"), xend = as.Date("2021-12-31"),
             y=2500, yend=2500, colour="red", linewidth = 2, arrow = arrow()) 
Error in group_by(., date): object 'covid_hospitalizations' not found

Well it looks like our hospitalization data tells a different story from the case report data! Something worth exploring in your assignment!


5.0.0 Class summary

That’s our first class! If we’ve made it this far, we’ve reviewed 1. Foundational concepts in R 2. Helpful functions in generating tidy data for analysis 3. Basics of visualizations using the ggplot2

We took a “messy” dataset from the Ontario government and created a tidy data set that we were able to visualize. We took that further by transforming the data into a 14-day sliding window of mean new cases per day in each public health unit. This clarified our picture of cases and visually confirmed that spread of SARS-CoV-2 did appear to be mitigated through lockdown orders.

Next week? Getting deeper into ggplot2!


5.1.0 Weekly assignment

This week’s assignment will be found under the current lecture folder under the “assignment” subfolder. It will include an R markdown notebook that you will use to produce the code and answers for this week’s assignment. Please provide answers in markdown or code cells that immediately follow each question section.

Assignment breakdown
Code 50% - Does it follow best practices?
- Does it make good use of available packages?
- Was data prepared properly
Answers and Output 50% - Is output based on the correct dataset?
- Are groupings appropriate
- Are correct titles/axes/legends correct?
- Is interpretation of the graphs correct?

Since coding styles and solutions can differ, students are encouraged to use best practices. Assignments may be rewarded for well-coded or elegant solutions.

You can save and download the Jupyter notebook in its native format. Submit this file to the the appropriate assignment section by 12:59 pm on the date of our next class: March 14th, 2024.


5.2.0 Acknowledgements

Revision 1.0.0: created and prepared for CSB1021H S LEC0141, 03-2021 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 1.0.1: edited and prepared for CSB1020H S LEC0141, 03-2022 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 1.0.2: edited and prepared for CSB1020H S LEC0141, 03-2023 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 2.0.0: Revised and prepared for CSB1020H S LEC0141, 03-2024 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.


6.0.0 Appendix 1: Instructions for installing your own software

6.1.0 R and RStudio

6.1.1 Installing R

As of 2022-03-01, the latest stable R version is 4.2.1:

Windows:
- Go to http://cran.utstat.utoronto.ca/
- Click on ‘Download R for Windows’
- Click on ‘install R for the first time’
- Click on ‘Download R 4.2.1 for Windows’ (or a newer version)
- Double-click on the .exe file once it has downloaded and follow the instructions.

(Mac) OS X:
- Go to http://cran.utstat.utoronto.ca/
- Click on ‘Download R for (Mac) OS X’
- Click on R-4.2.1 .pkg (or a newer version)
- Open the .pkg file once it has downloaded and follow the instructions.


Linux:
- Open a terminal (Ctrl + alt + t) - sudo apt-get update
- sudo apt-get install r-base
- sudo apt-get install r-base-dev (so you can compile packages from source)


6.1.2 Installing RStudio

As of 2023-03-01, the latest RStudio version is 2022.12.0+353 (released 2022-12-15)

Windows (10/11):
- Go to https://posit.co/downloads/
- Click on ‘RSTUDIO-2022.12.0-353.EXE’ to download the installer (or a newer version)
- Double-click on the .exe file once it has downloaded and follow the instructions.

(Mac) OS X (11+):
- Go to https://posit.co/downloads/
- Click on ‘RSTUDIO-2022.12.0-353.DMG’ to download the installer (or a newer version)
- Double-click on the .dmg file once it has downloaded and follow the instructions.


Linux:
- Go to https://posit.co/downloads/
- Click on the installer that describes your Linux distribution, e.g. ‘RSTUDIO-2022.12.0-353-AMD64.DEB’ (or a newer version)
- Double-click on the .deb file once it has downloaded and follow the instructions.
- If double-clicking on your .deb file did not open the software manager, open the terminal (Ctrl + alt + t) and type sudo dpkg -i /path/to/installer/RSTUDIO-2022.12.0-353-AMD64.deb

 _Note: You have 3 things that could change in this last command._     
 1. This assumes you have just opened the terminal and are in your home directory. (If not, you have to modify your path. You can get to your home directory by typing cd ~.)     
 2. This assumes you have downloaded the .deb file to Downloads. (If you downloaded the file somewhere else, you have to change the path to the file, or download the .deb file to Downloads).      
 3. This assumes your file name for .deb is the same as above. (Put the name matching the .deb file you downloaded).

If you have a problem with installing R or RStudio, you can also try to solve the problem yourself by Googling any error messages you get. You can also try to get in touch with me or the course TAs.


6.1.3 Getting to know the RStudio environment

RStudio is an IDE (Integrated Development Environment) for R that provides a more user-friendly experience than using R in a terminal setting. It has 4 main areas or panes, which you can customize to some extent under Tools > Global Options > Pane Layout:

  1. Source - The code you are annotating and keeping in your script.
  2. Console - Where your code is executed.
  3. Environment - What global objects you have created and functions you have written/sourced.
    History - A record of all the code you have executed in the console.
    Connections - Which data sources you are connecting to. (Not being used in this course.)
  4. Files, Plots, Packages, Help, Viewer - self-explanatoryish if you click on their tabs.

All of the panes can be minimized or maximized using the large and small box outlines in the top right of each pane.

6.1.3.1 Source

The Source is where you are keeping the code and annotation that you want to be saved as your script. The tab at the top left of the pane has your script name (i.e. ‘Untitled.R’), and you can switch between scripts by toggling the tabs. You can save, search or publish your source code using the buttons along the pane header. Code in the Source pane is run or executed automatically.

To run your current line of code or a highlighted segment of code from the Source pane you can:
a) click the button 'Run' -> 'Run Selected Line(s)',
b) click 'Code' -> 'Run Selected Line(s)' from the menu bar,
c) use the keyboard shortcut CTRL + ENTER (Windows & Linux) Command + ENTER (Mac) (recommended),
d) copy and paste your code into the Console and hit Enter (not recommended).

There are always many ways to do things in R, but the fastest way will always be the option that keeps your hands on the keyboard.

6.1.3.2 Console

You can also type and execute your code (by hitting ENTER) in the Console when the > prompt is visible. If you enter code and you see a + instead of a prompt, R doesn’t think you are finished entering code (i.e. you might be missing a bracket). If this isn’t immediately fixable, you can hit Esc twice to get back to your prompt. Using the up and down arrow keys, you can find previous commands in the Console if you want to rerun code or fix an error resulting from a typo.

On the Console tab in the top left of that pane is your current working directory. Pressing the arrow next to your working directory will open your current folder in the Files pane. If you find your Console is getting too cluttered, selecting the broom icon in that pane will clear it for you. The Console also shows information: upon start up about R (such as version number), during the installation of packages, when there are warnings, and when there are errors.

6.1.3.3 Environment

In the Global Environment you can see all of the stored objects you have created or sourced (imported from another script). The Global Environment can become cluttered, so it also has a broom button to clear its workspace.

Objects are made by using the assignment operator <-. On the left side of the arrow, you have the name of your object. On the right side you have what you are assigning to that object. In this sense, you can think of an object as a container. The container holds the values given as well as information about ‘class’ and ‘methods’ (which we will come back to).

Type x <- c(2,4) in the Console followed by Enter. 1D objects’ data types can be seen immediately as well as their first few values. Now type y <- data.frame(numbers = c(1,2,3), letters = c("a","b","c")) in the Console followed by Enter. You can immediately see the dimension of 2D objects, and you can check the structure of data frames and lists (more later) by clicking on the object’s arrow. Clicking on the object name will open the object to view in a new tab. Custom functions created in session or sourced will also appear in this pane.

The Environment pane dropdown displays all of the currently loaded packages in addition to the Global Environment. Loaded means that all of the tools/functions in the package are available for use. R comes with a number of packages pre-loaded (i.e. base, grDevices).

In the History tab are all of the commands you have executed in the Console during your session. You can select a line of code and send it to the Source or Console.

The Connections tab is to connect to data sources such as Spark and will not be used in this lesson.

6.1.3.4 Files, Plots, Packages, Help, Viewer

The Files tab allows you to search through directories; you can go to or set your working directory by making the appropriate selection under the More (blue gear) drop-down menu. The ... to the top left of the pane allows you to search for a folder in a more traditional manner.

The Plots tab is where plots you make in a .R script will appear (notebooks and markdown plots will be shown in the Source pane). There is the option to Export and save these plots manually.

The Packages tab has all of the packages that are installed and their versions, and buttons to Install or Update packages. A check mark in the box next to the package means that the package is loaded. You can load a package by adding a check mark next to a package, however it is good practice to instead load the package in your script to aid in reproducibility.

The Help menu has the documentation for all packages and functions. For each function you will find a description of what the function does, the arguments it takes, what the function does to the inputs (details), what it outputs, and an example. Some of the help documentation is difficult to read or less than comprehensive, in which case goggling the function is a good idea.

The Viewer will display vignettes, or local web content such as a Shiny app, interactive graphs, or a rendered html document.

6.1.3.5 Global Options

I suggest you take a look at Tools -> Global Options to customize your experience.

For example, under Code -> Editing I have selected Soft-wrap R source files followed by Apply so that my text will wrap by itself when I am typing and not create a long line of text.

You may also want to change the Appearance of your code. I like the RStudio theme: Modern and Editor font: Ubuntu Mono, but pick whatever you like! Again, you need to hit Apply to make changes.

That whirlwind tour isn’t everything the IDE can do, but it is enough to get started.


The Center for the Analysis of Genome Evolution and Function (CAGEF)

The Centre for the Analysis of Genome Evolution and Function (CAGEF) at the University of Toronto offers comprehensive experimental design, research, and analysis services in microbiome and metagenomic studies, genomics, proteomics, and bioinformatics.

From targeted DNA amplicon sequencing to transcriptomes, whole genomes, and metagenomes, from protein identification to post-translational modification, CAGEF has the tools and knowledge to support your research. Our state-of-the-art facility and experienced research staff provide a broad range of services, including both standard analyses and techniques developed by our team. In particular, we have special expertise in microbial, plant, and environmental systems.

For more information about us and the services we offer, please visit https://www.cagef.utoronto.ca/.

LS0tDQp0aXRsZTogIiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpgYGB7ciBlY2hvPUZBTFNFfQ0KIyBUaGlzIGFsbG93cyB0aGUgZmlsZSB0byBiZSBMSVZFIGFuZCBydW4gd2l0aG91dCBlcnJvcnMgc3RvcHBpbmcgaXQuDQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBUUlVFKQ0KYGBgDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovQ0FHRUZfc2VydmljZXNfc2xpZGUucG5nP3Jhdz10cnVlIiB3aWR0aD0iNzAwIi8+DQo6OjoNCg0KIyMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCiMjIExlY3R1cmUgMDE6ICJSIi1lZnJlc2hlciBvbiBSIGFuZCBiZXN0IHByYWN0aWNlcw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4xLjAgQW4gb3ZlcnZpZXcgb2YgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCioqIkFkdmFuY2VkIEdyYXBoaWNzIGFuZCBEYXRhIFZpc3VhbGl6YXRpb24gaW4gUiIqKiBpcyBicm91Z2h0IHRvIHlvdSBieSB0aGUgQ2VudHJlIGZvciB0aGUgQW5hbHlzaXMgb2YgR2Vub21lIEV2b2x1dGlvbiAmIEZ1bmN0aW9uJ3MgKENBR0VGKSBiaW9pbmZvcm1hdGljcyB0cmFpbmluZyBpbml0aWF0aXZlLiBDU0IxMDIxIHdhcyBkZXZlbG9wZWQgdG8gZW5oYW5jZSB0aGUgc2tpbGxzIG9mIHN0dWRlbnRzIHdpdGggYmFzaWMgYmFja2dyb3VuZHMgaW4gUiBieSBmb2N1c2luZyBvbiBhdmFpbGFibGUgcGhpbG9zb3BoaWVzLCBtZXRob2RzLCBhbmQgcGFja2FnZXMgZm9yIHBsb3R0aW5nIHNjaWVudGlmaWMgZGF0YS4gV2hpbGUgdGhlIGRhdGFzZXRzIGFuZCBleGFtcGxlcyB1c2VkIGluIHRoaXMgY291cnNlIHdpbGwgYmUgY2VudHJlZCBvbiBTQVJTLUNvVi0yIGRhdGFzZXRzLCB0aGUgdGVjaG5pcXVlcyBsZWFybmVkIGhlcmVpbiB3aWxsIGJlIGJyb2FkbHkgYXBwbGljYWJsZS4NCg0KVGhpcyBsZXNzb24gaXMgdGhlIGZpcnN0IGluIGEgNi1wYXJ0IHNlcmllcy4gVGhlIGFpbSBmb3IgdGhlIGVuZCBvZiB0aGlzIHNlcmllcyBpcyBmb3Igc3R1ZGVudHMgdG8gcmVjb2duaXplIGhvdyB0byBpbXBvcnQsIGZvcm1hdCwgYW5kIGRpc3BsYXkgZGF0YSBiYXNlZCBvbiB0aGVpciBpbnRlbmRlZCBtZXNzYWdlIGFuZCBhdWRpZW5jZS4gVGhlIGZvcm1hdCBhbmQgc3R5bGUgb2YgdGhlc2UgdmlzdWFsaXphdGlvbnMgd2lsbCBoZWxwIHRvIGlkZW50aWZ5IGFuZCBjb252ZXkgdGhlIGtleSBtZXNzYWdlKHMpIGZyb20gdGhlaXIgZXhwZXJpbWVudGFsIGRhdGEuDQoNClRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGNsYXNzIGlzIGEgKipjb2RlLWFsb25nIHN0eWxlKiogaW4gSnVweXRlciBub3RlYm9va3MuIEF0IHRoZSBzdGFydCBvZiBlYWNoIGxlY3R1cmUsIHNrZWxldG9uIHZlcnNpb25zIG9mIHRoZSBsZWN0dXJlIHdpbGwgYmUgcHJvdmlkZWQgZm9yIHVzZSBvbiB0aGUgW1VuaXZlcnNpdHkgb2YgVG9yb250byBKdXB5dGVyIEh1Yl0oaHR0cHM6Ly9qdXB5dGVyLnV0b3JvbnRvLmNhL2h1Yi9sb2dpbikgc28gc3R1ZGVudHMgY2FuIHByb2dyYW0gYWxvbmcgd2l0aCB0aGUgaW5zdHJ1Y3Rvci4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuMi4wIExlY3R1cmUgb2JqZWN0aXZlcw0KDQpUaGlzIHdlZWsgd2lsbCBiZSB5b3VyIGNyYXNoLWNvdXJzZSBvbiBKdXB5dGVyIG5vdGVib29rcyBhbmQgUiB0byByZWZyZXNoIG9uIHBhY2thZ2VzIGFuZCBwcmluY2lwbGVzIHRoYXQgd2lsbCBiZSByZWxldmFudCB0aHJvdWdob3V0IG91ciBjb3Vyc2UuIEluIG91ciBsZWN0dXJlcyBhbmQgeW91ciBhc3NpZ25tZW50cyB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aCBzb21lIHVuY3VyYXRlZCBkYXRhIHRvIHNpbXVsYXRlIHRoZSBmdWxsIGV4cGVyaWVuY2Ugb2Ygd29ya2luZyB3aXRoIGRhdGEgZnJvbSBzdGFydCB0byBmaW5pc2guIEl0J3MgaW1wb3J0YW50IHRoYXQgd2UgYXJlIGFsbCBmYW1pbGlhciB3aXRoLCBhbmQgdW5kZXJzdGFuZCB0aGUgbWFqb3JpdHkgb2YgdGhlIHRpZHkgZGF0YSBtZXRob2RzIHRoYXQgd2UnbGwgYmUgdXNpbmcgaW4gY2xhc3Mgc28gdGhhdCB3ZSBjYW4gZm9jdXMgb24gdGhlIG5ldyBtYXRlcmlhbCBhcyBpdCBhcHBlYXJzLiBXZSdsbCB1c2Ugc29tZSBzdGFuZGFyZCBwYWNrYWdlcyBhbmQgcHJhY3RpY2VzIHRvIGZpbmVzc2Ugb3VyIGRhdGEgYmVmb3JlIHZpc3VhbGl6aW5nIGl0LCBzbyBsZXQncyBSLWVmcmVzaCBvdXJzZWx2ZXMuDQoNCkF0IHRoZSBlbmQgb2YgdGhpcyBsZWN0dXJlIHdlIHdpbGwgaGF2ZSBjb3ZlcmVkIHRoZSBmb2xsb3dpbmcgdG9waWNzOg0KDQoxLiAgV29ya2luZyB3aXRoIEp1cHl0ZXIgbm90ZWJvb2tzIGFuZCBiZXN0IGNvZGluZyBwcmFjdGljZXMuDQoyLiAgUiBkYXRhIHR5cGVzLCBvYmplY3RzIGFuZCB3b3JraW5nIHdpdGggdGhlbS4NCjMuICBMb25nLWZvcm1hdCBhbmQgdGlkeSBkYXRhIHByaW5jaXBsZXMgdXNpbmcgdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2UuDQo0LiAgQmFzaWMgY29udHJvbCBmbG93IGFuZCBwbG90dGluZy4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuMy4wIEEgbGVnZW5kIGZvciB0ZXh0IGZvcm1hdCBpbiBKdXB5dGVyIG1hcmtkb3duDQoNCmBncmV5IGJhY2tncm91bmRgIC0gYSBwYWNrYWdlLCBmdW5jdGlvbiwgY29kZSwgY29tbWFuZCBvciBkaXJlY3RvcnkuIEJhY2t0aWNrcyBhcmUgYWxzbyB1c2UgZm9yIGluLWxpbmUgY29kZS5cDQoqaXRhbGljcyogLSBhbiBpbXBvcnRhbnQgdGVybSBvciBjb25jZXB0IG9yIGFuIGluZGl2aWR1YWwgZmlsZSBvciBmb2xkZXJcDQoqKmJvbGQqKiAtIGhlYWRpbmcgb3IgYSB0ZXJtIHRoYXQgaXMgYmVpbmcgZGVmaW5lZFwNCltibHVlIHRleHRde3N0eWxlPSJjb2xvcjpibHVlIn0gLSBuYW1lZCBvciB1bm5hbWVkIGh5cGVybGluaw0KDQpgLi4uYCAtIFdpdGhpbiBlYWNoIGNvZGluZyBjZWxsIHRoaXMgd2lsbCBpbmRpY2F0ZSBhbiBhcmVhIG9mIGNvZGUgdGhhdCBzdHVkZW50cyB3aWxsIG5lZWQgdG8gY29tcGxldGUgZm9yIHRoZSBjb2RlIGNlbGwgdG8gcnVuIGNvcnJlY3RseS4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1pbmZvfQ0KPGI+Qmx1ZSBib3g6PC9iPiBBIGtleSBjb25jZXB0IHRoYXQgaXMgYmVpbmcgaW50cm9kdWNlZA0KOjo6DQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCjxiPlllbGxvdyBib3g6PC9iPiBSaXNrIG9yIGNhdXRpb24NCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXN1Y2Nlc3N9DQo8Yj5HcmVlbiBib3hlczo8L2I+IFJlY29tbWVuZGVkIHJlYWRzIGFuZCByZXNvdXJjZXMgdG8gbGVhcm4gUHl0aG9uDQo6OjoNCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1kYW5nZXJ9DQo8Yj5SZWQgYm94ZXM6PC9iPiBBIGNvbXByZWhlbnNpb24gcXVlc3Rpb24gd2hpY2ggbWF5IG9yIG1heSBub3QgaW52b2x2ZSBhIGNvZGluZyBjZWxsLiBZb3UgdXN1YWxseSBmaW5kIHRoZXNlIGF0IHRoZSBlbmQgb2YgYSBzZWN0aW9uLg0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjQuMCBMZWN0dXJlIGFuZCBkYXRhIGZpbGVzIHVzZWQgaW4gdGhpcyBjb3Vyc2UNCg0KIyMjIDAuNC4xIFdlZWtseSBMZWN0dXJlIGFuZCBza2VsZXRvbiBmaWxlcw0KDQpFYWNoIHdlZWssIG5ldyBsZXNzb24gZmlsZXMgd2lsbCBhcHBlYXIgd2l0aGluIHlvdXIgSnVweXRlckh1YiBmb2xkZXJzLiBXZSBhcmUgcHVsbGluZyBmcm9tIGEgR2l0SHViIHJlcG9zaXRvcnkgdXNpbmcgdGhpcyBbUmVwb3NpdG9yeSBnaXQtcHVsbCBsaW5rXShodHRwczovL3IuZGF0YXRvb2xzLnV0b3JvbnRvLmNhL2h1Yi91c2VyLXJlZGlyZWN0L2dpdC1wdWxsP3JlcG89aHR0cHMlM0ElMkYlMkZnaXRodWIuY29tJTJGY2Ftb2slMkYyMDI0LTAzLUFkdl9HcmFwaGljc19SJnVybHBhdGg9cnN0dWRpbyUyRiZicmFuY2g9bWFpbikuIFNpbXBseSBjbGljayBvbiB0aGUgbGluayBhbmQgaXQgd2lsbCB0YWtlIHlvdSB0byB0aGUgW1VuaXZlcnNpdHkgb2YgVG9yb250byBKdXB5dGVySHViXShodHRwczovL2RhdGF0b29scy51dG9yb250by5jYSkuIFlvdSB3aWxsIG5lZWQgdG8gdXNlIHlvdXIgVVRPUmlkIGNyZWRlbnRpYWxzIHRvIGNvbXBsZXRlIHRoZSBsb2dpbiBwcm9jZXNzLiBGcm9tIHRoZXJlIHlvdSB3aWxsIGZpbmQgZWFjaCB3ZWVrJ3MgbGVjdHVyZSBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5IGAvMjAyNC0wMy1BZHZfR3JhcGhpY3NfUi9MZWN0dXJlX1hYYC4gWW91IHdpbGwgZmluZCBhIHBhcnRpYWxseSBjb2RlZCBgc2tlbGV0b24ucm1kYCBmaWxlIGFzIHdlbGwgYXMgYWxsIG9mIHRoZSBkYXRhIGZpbGVzIG5lY2Vzc2FyeSB0byBydW4gdGhlIHdlZWsncyBsZWN0dXJlLg0KDQpBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIGRvd25sb2FkIHRoZSBSLU1hcmtkb3duIE5vdGVib29rIChgLlJtZGApIGFuZCBkYXRhIGZpbGVzIGZyb20gdGhlIFJTdHVkaW8gc2VydmVyIHRvIHlvdXIgcGVyc29uYWwgY29tcHV0ZXIgaWYgeW91IHdvdWxkIGxpa2UgdG8gcnVuIGluZGVwZW5kZW50bHkgb2YgdGhlIFRvcm9udG8gdG9vbHMuDQoNCiMjIyAwLjQuMiBMaXZlLWNvZGluZyBIVE1MIHBhZ2UNCg0KQSBsaXZlIGxlY3R1cmUgdmVyc2lvbiB3aWxsIGJlIGF2YWlsYWJsZSBhdCBbY2Ftb2suZ2l0aHViLmlvXShodHRwczovL2NhbW9rLmdpdGh1Yi5pby8yMDI0LTAzLkFkdl9HcmFwaGljc19SL2luZGV4Lmh0bWwpIHRoYXQgd2lsbCB1cGRhdGUgYXMgdGhlIGxlY3R1cmUgcHJvZ3Jlc3Nlcy4gQmUgc3VyZSB0byByZWZyZXNoIHRvIHRha2UgYSBsb29rIGlmIHlvdSBnZXQgbG9zdCENCg0KIyMjIDAuNC4zIFBvc3QtbGVjdHVyZSBQREZzIGFuZCBSZWNvcmRpbmdzDQoNCkFzIG1lbnRpb25lZCBhYm92ZSwgYXQgdGhlIGVuZCBvZiBlYWNoIGxlY3R1cmUgdGhlcmUgd2lsbCBiZSBhIGNvbXBsZXRlZCB2ZXJzaW9uIG9mIHRoZSBsZWN0dXJlIGNvZGUgcmVsZWFzZWQgYXMgYSBQREYgZmlsZSB1bmRlciB0aGUgTW9kdWxlcyBzZWN0aW9uIG9mIFF1ZXJjdXMuDQoNCiMjIyAwLjQuNCBEYXRhIHVzZWQgaW4gdGhpcyBsZXNzb24NCg0KVG9kYXkncyBkYXRhc2V0cyB3aWxsIGZvY3VzIG9uIGVwaWRlbWlvbG9naWNhbCBkYXRhIGZyb20gdGhlIE9udGFyaW8gcHJvdmluY2lhbCBnb3Zlcm5tZW50IGZvdW5kIFtoZXJlXShodHRwczovL2RhdGEub250YXJpby5jYS9kYXRhc2V0L3N0YXR1cy1vZi1jb3ZpZC0xOS1jYXNlcy1pbi1vbnRhcmlvKSBhbmQgW2hlcmVdKGh0dHBzOi8vZGF0YS5vbnRhcmlvLmNhL2VuL2RhdGFzZXQvY292aWQtMTktY2FzZXMtaW4taG9zcGl0YWwtYW5kLWljdS1ieS1vbnRhcmlvLWhlYWx0aC1yZWdpb24pLg0KDQojIyMgMC40LjQuMSBEYXRhc2V0IDE6IE9udGFyaW9fZGFpbHlfY2hhbmdlX2luX2Nhc2VzX2J5X3BodS5jc3YNCg0KVGhpcyBkYXRhc2V0IHdhcyBvYnRhaW5lZCBmcm9tIHRoZSBPbnRhcmlvIHByb3ZpbmNpYWwgd2Vic2l0ZSBhbmQgaG9sZHMgc3RhdGlzdGljcyByZWdhcmRpbmcgU0FSUy1Db1YtMiBjYXNlcyB0aHJvdWdob3V0IGRpZmZlcmVudCBwdWJsaWMgaGVhbHRoIHVuaXRzIGluIHRoZSBwcm92aW5jZS4gSXQgaXMgaW4gYSBjb21tYSBzZXBhcmF0ZSBmb3JtYXQgYW5kIGhhcyBiZWVuIGNvbGxlY3RlZCBzaW5jZSAyMDIwLTAzLTI0IHRocm91Z2ggMjAyMy0wMS0yNS4NCg0KIyMjIDAuNC40LjIgRGF0YXNldCAyOiByZWdpb25faG9zcGl0YWxfaWN1X2NvdmlkX2RhdGEuY3N2DQoNClRoaXMgZGF0YXNldCB3YXMgb2J0YWluZWQgZnJvbSB0aGUgT250YXJpbyBwcm92aW5jaWFsIHdlYnNpdGUgYW5kIGhvbGRzIGRhdGEgcmVnYXJkaW5nIFNBUlMtQ29WLTIgdGhyb3VnaG91dCA1IE9udGFyaW8gaGVhbHRoIHJlZ2lvbnMuIEl0IGlzIGluIGEgY29tbWEtc2VwYXJhdGVkIGZvcm1hdCBhbmQgaGFzIGJlZW4gZ3Jvd2luZy9leHBhbmRpbmcgc2luY2UgaW5pdGlhbCB0cmFja2luZyBzdGFydGVkIG9uIDIwMjAtMDQtMDIgdGhyb3VnaCAyMDIzLTAyLTIzLiBUaGlzIGlzIGEgZGF0YXNldCBvbmx5IHRyYWNrcyA3IHZhcmlhYmxlcyBzcGVjaWZpY2FsbHkgcmVnYXJkaW5nIHRoZSBkYWlseSB0b3RhbHMgb2YgaG9zcGl0YWxpemVkIENPVklELTE5IHBhdGllbnRzIGJvdGggaW4gdGhlIElDVSBhbmQgZ2VuZXJhbCBjYXJlLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC41LjAgUGFja2FnZXMgdXNlZCBpbiB0aGlzIGxlc3Nvbg0KDQpgdGlkeXZlcnNlYCB3aGljaCBoYXMgYSBudW1iZXIgb2YgcGFja2FnZXMgaW5jbHVkaW5nIGBkcGx5cmAsIGB0aWR5cmAsIGBzdHJpbmdyYCwgYGZvcmNhdHNgIGFuZCBgZ2dwbG90MmANCg0KYHZpcmlkaXNgIGhlbHBzIHRvIGNyZWF0ZSBjb2xvci1ibGluZCBwYWxldHRlcyBmb3Igb3VyIGRhdGEgdmlzdWFsaXphdGlvbnMNCg0KYGx1YnJpZGF0ZWAgYW5kIGB6b29gIGFyZSBoZWxwZXIgcGFja2FnZXMgdXNlZCBmb3Igd29ya2luZyB3aXRoIGRhdGUgZm9ybWF0cyBpbiBSDQoNCkxldCdzIHJ1biBvdXIgZmlyc3QgY29kZSBjZWxsIQ0KDQpgYGB7cn0NCiMgUGFja2FnZXMgdG8gaGVscCB0aWR5IG91ciBkYXRhDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KIyBQYWNrYWdlcyBmb3IgdGhlIGdyYXBoaWNhbCBhbmFseXNpcyBzZWN0aW9uDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCiMgcGFja2FnZXMgdXNlZCBmb3Igd29ya2luZyB3aXRoL2Zvcm1hdGluZyBkYXRlcyBpbiBSDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoem9vKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDEuMC4wIENvZGluZyBpbiBSIE1hcmtkb3duIE5vdGVib29rcw0KDQpXb3JrIHdpdGggeW91ciBSIG1hcmtkb3duIG5vdGVib29rIG9uIHRoZSBVbml2ZXJzaXR5IG9mIFRvcm9udG8gZGF0YXRvb2xzIGh1YiB3aWxsIGFsbCBiZSBjb250YWluZWQgd2l0aGluIGEgbmV3IGJyb3dzZXIgdGFiIHdpdGggdGhlIGFkZHJlc3MgYmFyIHNob3dpbmcgc29tZXRoaW5nIHNpbWlsYXIgdG8NCg0KYGh0dHBzOi8vci5kYXRhdG9vbHMudXRvcm9udG8uY2EvdXNlci9jYWx2aW4ubW9rQHV0b3JvbnRvLmNhL3JzdHVkaW8vYA0KDQpBbGwgb2YgdGhpcyBpcyBydW5uaW5nIHJlbW90ZWx5IG9uIGEgVW5pdmVyc2l0eSBvZiBUb3JvbnRvIHNlcnZlciByYXRoZXIgdGhhbiB5b3VyIG93biBtYWNoaW5lLg0KDQpZb3UnbGwgc2VlIGEgZGlyZWN0b3J5IHN0cnVjdHVyZSBmcm9tIHlvdXIgaG9tZSBmb2xkZXI6DQoNCmllIGAvaG9tZS9yc3R1ZGlvLzIwMjQtMDMtQWR2X0dyYXBoaWNzX1IvYCBhbmQgYSBmb2xkZXIgdG8gYExlY3R1cmVfMDFfUl9JbnRyb2R1Y3Rpb25gIHdpdGhpbi4gQ2xpY2tpbmcgb24gdGhhdCwgeW91J2xsIGZpbmQgYExlY3R1cmVfMDEuUi1lZnJlc2hlci5za2VsZXRvbi5SbWRgIHdoaWNoIGlzIHRoZSBub3RlYm9vayB3ZSB3aWxsIHVzZSBmb3IgdG9kYXkncyBjb2RlLWFsb25nIGxlY3R1cmUuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjEuMCBXaHkgaXMgdGhpcyBjbGFzcyB1c2luZyBSIE1hcmtkb3duIE5vdGVib29rcz8NCg0KV2UndmUgaW1wbGVtZW50ZWQgdGhlIGNsYXNzIHRoaXMgd2F5IHRvIHJlZHVjZSB0aGUgYnVyZGVuIG9mIGhhdmluZyB0byBpbnN0YWxsIHZhcmlvdXMgcHJvZ3JhbXMuIFdoaWxlIGluc3RhbGxhdGlvbiBjYW4gYmUgYSAqbGl0dGxlKiB0cmlja3ksIGl0J3MgcmVhbGx5IG5vdCB0aGF0IGJhZC4gRm9yIHRoaXMgY291cnNlLCBob3dldmVyLCB5b3UgZG9uJ3QgbmVlZCB0byBnbyB0aHJvdWdoIGFsbCBvZiB0aGF0IGp1c3QgdG8gaW1wcm92ZSBvbiB5b3VyIGRhdGEgdmlzdWFsaXphdGlvbiBza2lsbHMuDQoNClIgbWFya2Rvd24gbm90ZWJvb2tzIGFsc28gZ2l2ZSB1cyB0aGUgb3B0aW9uIG9mIGluc2VydGluZyAibWFya2Rvd24iIHRleHQgbXVjaCBsaWtlIHdoYXQgeW91J3JlIHJlYWRpbmcgYXQgdGhpcyB2ZXJ5ICpleGFjdCogbW9tZW50LiBTbyB3ZSBjYW4gaW50ZXJzcGVyc2UgaWRlYXMgYW5kIGluZm9ybWF0aW9uIGJldHdlZW4gb3VyIGxlYXJuaW5nIGNvZGUgYmxvY2tzLg0KDQpUaGVyZSBpcywgaG93ZXZlciBhbiBhcHBlbmRpeCBzZWN0aW9uIGF0IHRoZSBlbmQgb2YgdGhpcyBsZWN0dXJlIGRldGFpbGluZyBob3cgdG8gaW5zdGFsbCB0aGUgUi1rZXJuZWwgaXRzZWxmIGFuZCB0aGUgaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCAoSURFKSBjYWxsZWQgUlN0dWRpby4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuMi4wIFBhY2thZ2VzIGNvbnRhaW4gdXNlZnVsIGZ1bmN0aW9ucyB0aGF0IHdlJ2xsIHVzZSBvZnRlbg0KDQpTby4uLiB3aGF0IGFyZSBpbiB0aGVzZSBwYWNrYWdlcz8gQSAqKnBhY2thZ2UqKiBjYW4gYmUgYSBjb2xsZWN0aW9uIG9mIC0gZnVuY3Rpb25zIC0gZGF0YSBvYmplY3RzIC0gY29tcGlsZWQgY29kZSAtIGZ1bmN0aW9ucyB0aGF0ICpvdmVycmlkZSogYmFzZSBmdW5jdGlvbnMgaW4gUg0KDQoqKkZ1bmN0aW9ucyoqIGFyZSB0aGUgYmFzaWMgd29ya2hvcnNlcyBvZiBSOyB0aGV5IGFyZSB0aGUgdG9vbHMgd2UgdXNlIHRvIGFuYWx5emUgb3VyIGRhdGEuIEVhY2ggZnVuY3Rpb24gY2FuIGJlIHRob3VnaHQgb2YgYXMgYSB1bml0IHRoYXQgaGFzIGEgc3BlY2lmaWMgdGFzay4gQSBmdW5jdGlvbiB0YWtlcyBhbiBpbnB1dCwgZXZhbHVhdGVzIGl0IHVzaW5nIGFuIGV4cHJlc3Npb24gKGUuZy4gYSBjYWxjdWxhdGlvbiwgcGxvdCwgbWVyZ2UsIGV0Yy4pLCBhbmQgcmV0dXJucyBhbiBvdXRwdXQgKGEgc2luZ2xlIHZhbHVlLCBtdWx0aXBsZSB2YWx1ZXMsIGEgZ3JhcGhpYywgZXRjLikuDQoNCkluIHRoaXMgY291cnNlIHdlIHdpbGwgZnJlcXVlbnRseSByZWx5IG9uIGEgcGFja2FnZSBjYWxsZWQgYHRpZHl2ZXJzZWAgd2hpY2ggaXMgYWxzbyBjb21wb3NlZCBvZiBhIHNlcmllcyBvZiBvdGhlciBwYWNrYWdlcyB3ZSBjYW4gdXNlIHRvIHJlZm9ybWF0IG91ciBkYXRhIGxpa2UgYHJlYWRyYCwgYGRwbHlyYCwgYHRpZHlyYCBhbmQgYHN0cmluZ3JgLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS4zLjAgUiBtYXJrZG93biBub3RlYm9va3MgcnVuIHRoZSBwcm9ncmFtbWluZyBsYW5ndWFnZSBSDQoNCkJlaGluZCB0aGUgc2NlbmVzIG9mIGVhY2ggbWFya2Rvd24gbm90ZWJvb2sgdGhlIFIga2VybmVsIGlzIHJ1bm5pbmcuIEFzIHdlIG1vdmUgZnJvbSBjb2RlIGNlbGwgdG8gbmV3IGNvZGUgY2VsbCwgYWxsIG9mIHRoZSB2YXJpYWJsZXMgb3Igb2JqZWN0cyB3ZSBoYXZlIGNyZWF0ZWQgYXJlIHN0b3JlZCB3aXRoaW4gbWVtb3J5LiBXZSBjYW4gcmVmZXIgdG8gdGhlc2UgYXMgd2UgcnVuIHRoZSBjb2RlIGFuZCBtb3ZlIGZvcndhcmQgYnV0IGlmIHlvdSBvdmVyd3JpdGUgb3IgY2hhbmdlIHRoZW0gYnkgbWlzdGFrZSwgeW91IG1heSB0byBoYXZlIHJlcnVuIG11bHRpcGxlIGNlbGwgYmxvY2tzIQ0KDQpUaGVyZSBhcmUgc29tZSBvcHRpb25zIGluIHRoZSAiQ29kZSIgbWVudSB0aGF0IGNhbiBhbGxldmlhdGUgdGhlc2UgcHJvYmxlbXMgc3VjaCBhcyAiUnVuIFJlZ2lvbiBcPiBSdW4gQWxsIENodW5rcyBBYm92ZSIuIElmIHlvdSB0aGluayB5b3UndmUgbWFkZSBhIGJpZyBlcnJvciBieSBvdmVyd3JpdGluZyBhIGtleSBvYmplY3QsIHlvdSBjYW4gdXNlIHRoYXQgb3B0aW9uIHRvICJyZS1pbml0aWFsaXplIiBhbGwgb2YgeW91ciBwcmV2aW91cyBjb2RlIQ0KDQpUaGUgcnVuIG9yZGVyIG9mIHlvdXIgY29kZSBpcyBhbHNvIHZpc2libGUgYXQgdGhlIHNpZGUgb2YgZWFjaCBjb2RlIGNlbGwgYXMgYFt4XWAuIFdoZW4gYSBjb2RlIGNlbGwgaXMgc3RpbGwgYWN0aXZlbHkgcnVubmluZyBpdCB3aWxsIGJlIGRlbm90ZWQgYXMgYFsqXWAgc2luY2UgYSBudW1iZXIgY2Fubm90IGJlIGFzc2lnbmVkIHRvIGl0LiBZb3UnbGwgYWxzbyBub3RpY2UgeW91ciBrZXJuZWwgKHRvcCByaWdodCBvZiB0aGUgbWVudSBiYXIpIGhhcyBhIHNtYWxsIGNpcmNsZSB0aGF0IHdpbGwgYmUgZGFyayB3aGlsZSBydW5uaW5nLCBhbmQgY2xlYXIgd2hpbGUgaWRsZS4NCg0KKipSZW1lbWJlciB0aGVzZSBmcmllbmRseSBrZXlzL3Nob3J0Y3V0czoqKg0KDQotICAgYEFycm93YCBrZXlzIHRvIG5hdmlnYXRlIHVwIGFuZCBkb3duIChhbmQgd2l0aGluIGEgY2VsbCkNCi0gICBgQ3RybGArYFNoaWZ0YCtgRW50ZXJgIHRvIHJ1biBhIGNlbGwgKGJvdGggY29kZSBhbmQgbWFya2Rvd24pDQotICAgYEFsdGArYEN0cmxgK2BFbnRlcmAgdG8gcnVuIHRoZSBuZXh0IGNlbGwNCi0gICBgQ3RybGArYFNoaWZ0YCtgQ2AgdG8gcXVpY2tseSBjb21tZW50IGFuZCB1bmNvbW1lbnQgc2luZ2xlIG9yIG11bHRpcGxlIGxpbmVzIG9mIGNvZGUNCi0gICBgVGFiYCBjYW4gYmUgdXNlZCB3aGlsZSBjb2RpbmcgdG8gYXV0b2NvbXBsZXRlIHZhcmlhYmxlLCBmdW5jdGlvbiBhbmQgZmlsZSBuYW1lcywgYW5kIGV2ZW4gbG9vayBhdCBhIGxpc3Qgb2YgcG9zc2libGUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb25zLg0KLSAgIGBDdHJsYCtgQWx0YCtgSWAgdG8gaW5zZXJ0IGEgbmV3IGNvZGluZyBjZWxsDQoNCiMjIyAxLjMuMyBXaHkgd291bGQgeW91IHdhbnQgdG8gdXNlIGEgTWFya2Rvd24gTm90ZWJvb2s/DQoNCkRlcGVuZGluZyBvbiB5b3VyIG5lZWRzLCB5b3UgbWF5IGZpbmQgeW91cnNlbGYgZG9pbmcgdGhlIGZvbGxvd2luZzoNCg0KLSAgIEFuYWx5c2luZyBkYXRhIGZvciB5b3VyIHByb2plY3QgdXNpbmcgYXZhaWxhYmxlIHBhY2thZ2VzDQotICAgUmUtYW5hbHlzaW5nIGRhdGEgZm9yIHlvdXIgcHJvamVjdA0KLSAgIEFuYWx5c2luZyBtdWx0aXBsZSBkYXRhc2V0cyBmb3IgeW91ciBwcm9qZWN0DQotICAgQ29sbGFib3JhdGluZyBvbiBkYXRhIGFuZCBhbmFseXNlcyBmb3IgeW91ciBwcm9qZWN0DQotICAgKipFeHBsYWluaW5nKiogeW91ciBkYXRhIGFuZCBhbmFseXNlcyB0byBhIHN1cGVydmlzb3Igb3IgY29sbGFib3JhdG9yIQ0KDQpNYXJrZG93biBhbGxvd3MgeW91IHRvIGFsdGVybmF0ZSBiZXR3ZWVuICJtYXJrZG93biIgbm90ZXMgYW5kICJjb2RlIiB0aGF0IGNhbiBiZSBydW4gb3IgcmUtcnVuIG9uIHRoZSBmbHkuDQoNCkVhY2ggZGF0YSBydW4gYW5kIGl0J3MgcmVzdWx0cyBjYW4gYmUgc2F2ZWQgaW5kaXZpZHVhbGx5IGFzIGEgbmV3IG5vdGVib29rIHRvIGNvbXBhcmUgZGF0YSBhbmQgc21hbGwgY2hhbmdlcyB0byBhbmFseXNlcyENCg0KIyMjIDEuMy40IFdoYXQgaXMgbWFya2Rvd24gbGFuZ3VhZ2U/DQoNCk1hcmtkb3duIGlzIGEgbWFya3VwIGxhbmd1YWdlIHRoYXQgbGV0cyB5b3Ugd3JpdGUgSFRNTCBhbmQgSmF2YSBTY3JpcHQgY29kZSBpbiBjb21iaW5hdGlvbiB3aXRoIG90aGVyIGxhbmd1YWdlcy4gVGhpcyBhbGxvd3MgeW91IHRvIG1ha2UgaHRtbCwgcGRmLCBhbmQgdGV4dCBkb2N1bWVudHMgdGhhdCBhcmUgY29tYmluYXRpb25zIG9mIHRleHQgYW5kIGNvZGUsIGVuaGFuY2luZyByZXByb2R1Y2liaWxpdHksIGEga2V5IGFzcGVjdCBpbiBzY2llbnRpZmljIHdvcmsuIEhhdmluZyBldmVyeXRoaW5nIGluIGEgc2luZ2xlIHBsYWNlIGFsc28gYm9vc3RzIHByb2R1Y3Rpdml0eSBkdXJpbmcgcmVzdWx0cyBpbnRlcnByZXRhdGlvbiAtIG5vIG5lZWQgdG8gZ28gYmFjayBhbmQgZm9ydGggYmV0d2VlbiB0YWJzLCBwYWdlcywgYW5kIGRvY3VtZW50cy4gVGhleSBjYW4gYWxsIGJlIGludGVncmF0ZWQgaW4gYSBzaW5nbGUgZG9jdW1lbnQsIGFsbG93aW5nIGZvciBhIG1vcmUgZmx1aWQgbmFycmF0aXZlIG9mIHRoZSBzdG9yeSB0aGF0IHlvdSBhcmUgY29tbXVuaWNhdGluZyB0byB5b3VyIGF1ZGllbmNlIChsZXNzIGRpc3RyYWN0aW9ucyBmb3IgeW91ISkuIEZvciBleGFtcGxlLCB0aGUgbGluZXMgb2YgY29kZSBiZWxvdyBhbmQgdGhlIHRleHQgeW91IGFyZSByZWFkaW5nIHJpZ2h0IG5vdyB3ZXJlIGNyZWF0ZWQgaW4gUidzIE1hcmtkb3duIGxhbmd1YWdlLiAoRG8gbm90IHdvcnJ5IGFib3V0IHRoZSBSIGNvZGUganVzdCB5ZXQuIFdlIHdpbGwgZ2V0IHRoZXJlIHNvb25lciB0aGFuIHlvdSB0aGluaykuDQoNCkFzIG1lbnRpb25lZCwgbWFya2Rvd24gYWxzbyBhbGxvd3MgeW91IHRvIHdyaXRlIGluIFtMYVRlWF0oaHR0cHM6Ly93d3cubGF0ZXgtcHJvamVjdC5vcmcvYWJvdXQvKSwgYSBkb2N1bWVudCBwcmVwYXJhdGlvbiBzeXN0ZW0gdG8gd3JpdGUgbWF0aGVtYXRpY2FsIG5vdGF0aW9uLiBBbGwgaXQgdGFrZXMgaXMgdG8gd3JhcCBMYVRlWCBjb2RlIGJldHdlZW4gc2luZ2xlIGRvbGxhciBzaWducyAoXCQpIGZvciBpbmxpbmUgbm90YXRpb24gb3IgdHdvIGRvdWJsZSBkb2xsYXIgc2lnbnMgKFwkXCQpLCBvbmUgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgZXF1YXRpb24gYW5kIG9uZSBhdCB0aGUgZW5kLiBGb3IgZXhhbXBsZSwgdGhlIGVxdWF0aW9uICoqKllpID0gYmV0YTAgKyBiZXRhMSB4aSArIGVwc2lsb25faSwgaT0xLCAuLi4sIE4qKiogY2FuIGJlIHRyYW5zZm9ybWVkIGludG8gTGFUZVggY29kZSBieSBhZGRpbmcgc29tZSBjaGFyYWN0ZXJzOiBcKlwqXCpZX2kgPSBcYmV0YVxfMCArIFxiZXRhXF8xIHhfaSArIFx2YXJlcHNpbG9uXF9pLCBpPTEsIFxkb3RzLCBOXCpcKlwqLiBOb3csIGlmIHdlIHVzZSBcJFwkIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIExhVGVYIGNvZGUsIHRoaXMgaXMgd2hhdCB3ZSBnZXQ6DQoNCiQkIA0KWV9pID0gXGJldGFfMCArIFxiZXRhXzEgeF9pICsgXHZhcmVwc2lsb25faSwgaT0xLCBcZG90cyxOIA0KJCQNCg0KU2VlPyBKdXN0IGxpa2UgdGhhdCEgSGVyZSBpcyBhbiBleGFtcGxlIG9mIGEgdGFibGUgbWFkZSBpbiBNYXJrZG93biwgc2hvd2luZyBzb21lIG9mIHRoZSBtb3N0IHBvcHVsYXIgUiBsaWJyYXJpZXMgZm9yIGRhdGEgc2NpZW5jZToNCg0KfCBMaWJyYXJ5ICAgfCBVc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgdGlkeXZlcnNlIHwgU2ltcGxpZmllZCB0YWJ1bGFyLWRhdGEgcHJvY2Vzc2luZyBmdW5jdGlvbnMgICAgICAgICAgICAgICAgICAgfA0KfCBnZ3Bsb3QyICAgfCBEYXRhIHZpc3VhbGl6YXRpb24gcGFja2FnZSB0eXBpY2FsbHkgaW5jbHVkZWQgaW4gdGhlIHRpZHl2ZXJzZSB8DQp8IHNoaW55ICAgICB8IFVzZWQgdG8gY3JlYXRlIGludGVyYWN0aXZlIFItYmFzZWQgd2ViIHBhZ2VzIGFuZCBpbnRlcmZhY2VzICAgIHwNCnwgY2FyICAgICAgIHwgUG9wdWxhciBzdGF0aXN0aWNhbCBhbmFseXNpcyB3aXRoIFR5cGUgSUkgYW5kIElJSSBBTk9WQSB0YWJsZXMgfA0KDQpUaGVzZSBhcmUganVzdCBhIGZldyBleGFtcGxlcyBvZiB3aGF0IHlvdSBjYW4gZG8gd2l0aCBKdXB5dGVyIGFuZCBNYXJrZG93bi4gVG8gZmluZCBvdXQgbW9yZSBvbiBob3cgdG8gZ2V0IHRoZSBiZXN0IG9mIE1hcmtkb3duLCBoZWFkIG9uIG92ZXIgdG8gdGhlIFtSIE1hcmtkb3duIGNvb2tib29rXSAoPGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi1jb29rYm9vay8+KS4NCg0KT25jZSB5b3UgYXJlIGZpbmlzaGVkIHdyaXRpbmcgeW91ciBjb2RlIGFuZCBpbnRlcnByZXRpbmcgdGhvc2UgcmVzdWx0cyBpbiBhIG1hcmtkb3duIG5vdGVib29rLCB5b3UgY2FuIHJlbmRlciB0aGUgbm90ZWJvb2sgaW50byBwZGYsIGh0bWwsIGFuZCBtYW55IG90aGVyIGZvcm1hdHMuIFRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gYWNoaWV2ZSB0aGlzLiBUaGUgZWFzaWVzdCBvcHRpb24gaXMgdG8gZ28gdG8gKipGaWxlIFw+IEtuaXQgRG9jdW1lbnQqKi4gQWZ0ZXJ3YXJkcyB0aGVyZSBzaG91bGQgYmUgYW4gb3B0aW9uIHRvIHZpZXcgaW4gYnJvd3NlciBhdCB3aGljaCBwb2ludCB5b3UgY2FuIHNhdmUgYXMgYW4gSFRNTCBvciBwcmludCBpdCB0byBQREYuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjQuMCBGb2xsb3dpbmcgYmVzdCBwcmFjdGljZXMgZm9yIGNvZGluZyB3aWxsIG1ha2UgbGlmZSBlYXNpZXINCg0KTGV0J3MgZGlzY3VzcyBzb21lIGltcG9ydGFudCBiZWhhdmlvdXJzIGJlZm9yZSB3ZSBiZWdpbiBjb2Rpbmc6IC0gQ29kZSBhbm5vdGF0aW9uIChjb21tZW50aW5nKSAtIFZhcmlhYmxlIG5hbWluZyBjb252ZW50aW9ucyAtIEJlc3QgcHJhY3RpY2VzDQoNCiMjIyAxLjQuMSBBbm5vdGF0ZSB5b3VyIGNvZGUgd2l0aCB0aGUgYCNgIHN5bWJvbA0KDQoqKldoeSBib3RoZXI/KioNCg0KLSAgICJDYW4geW91IHJlcnVuIHRoaXMgYW5hbHlzaXMgYW5kIGJ1dCBjaGFuZ2UgWCBwYXJhbWV0ZXI/IiAtICpBbm9ueW1vdXMgUEkqXA0KLSAgICJDYW4geW91IG1ha2UgdGhpcyBwbG90LCBidXQgd2l0aCBkYXNoZWQgbGluZXMsIGEgZGlmZmVyZW50IGF4aXMsIHdpdGggZXJyb3IgYmFycz8iIC0gKkFub255bW91cyBsYWJtYXRlKg0KLSAgICJDYW4gSSBib3Jyb3cgeW91ciBjb2RlPyIgLSAqQW5vbnltb3VzIGNvbGxhYm9yYXRvciBvciBvZmZpY2VtYXRlIG9yIFBJKlwNCi0gICAiV2h5IGlzIHRoYXQgb2JqZWN0IGJlaW5nIHNlbnQgdG8gdGhhdCBmdW5jdGlvbj8gV2hhdCBpcyBpdCByZXR1cm5pbmc/IiAtICpZb3UsIE1lLCBhbmQgYW55b25lIHJlYWRpbmcgeW91ciBjb2RlKg0KDQoqKllvdXIgd29yc3QgY29sbGFib3JhdG9yIGlzIHBvdGVudGlhbGx5IHlvdSBpbiA2IGRheXMgb3IgNiBtb250aHMuIERvIHlvdSByZW1lbWJlciB3aGF0IHlvdSBoYWQgZm9yIGJyZWFrZmFzdCBsYXN0IFR1ZXNkYXk/KioNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9MT1RSX2VsdmlzaC5wbmc/cmF3PXRydWUiIHdpZHRoPSI3MDAiLz4NCjo6Og0KDQpDcmVkaXQ6IDxodHRwczovL3d3dy50ZXN0Ynl0ZXMubmV0L2Jsb2cvcHJvZ3JhbW1pbmctbWVtZXMvPg0KDQpZb3UgY2FuIGFubm90YXRlIHlvdXIgY29kZSBmb3Igc2VsZmlzaCByZWFzb25zLCBvciBhbHRydWlzdGljIHJlYXNvbnMsIGJ1dCBhbm5vdGF0ZSB5b3VyIGNvZGUuDQoNCioqSG93IGRvIEkgc3RhcnQ/KioNCg0KLSAgIEl0IGlzLCBpbiBnZW5lcmFsLCBwYXJ0IG9mIGJlc3QgY29kaW5nIHByYWN0aWNlcyB0byBrZWVwIHRoaW5ncyB0aWR5IGFuZCBvcmdhbml6ZWQuDQoNCi0gICBBIGhhc2gtdGFnIGAjYCB3aWxsIGNvbW1lbnQgeW91ciB0ZXh0LiBJbnNpZGUgYSBjb2RlIGNlbGwgaW4gYSBKdXB5dGVyIE5vdGVib29rIG9yIGFueXdoZXJlIGluIGFuIFIgc2NyaXB0LCBhbGwgdGV4dCAqYWZ0ZXIqIGEgaGFzaHRhZyB3aWxsIGJlIGlnbm9yZWQgYnkgUiBhbmQgYnkgbWFueSBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMuIEl0J3MgdmVyeSB1c2VmdWwgdG8gYWRkIGNvbW1lbnRzIGFib3V0IGNoYW5nZXMgaW4geW91ciBjb2RlLCBhcyB3ZWxsIGFzIGRldGFpbGVkIGV4cGxhbmF0aW9ucyBhYm91dCB5b3VyIHNjcmlwdHMuDQoNCi0gICBQdXQgYSBkZXNjcmlwdGlvbiBvZiB3aGF0IHlvdSBhcmUgZG9pbmcgbmVhciB5b3VyIGNvZGUgYXQgZXZlcnkgcHJvY2VzcywgZGVjaXNpb24gcG9pbnQsIG9yIG5vbi1kZWZhdWx0IGFyZ3VtZW50IGluIGEgZnVuY3Rpb24uIEZvciBleGFtcGxlLCB3aHkgeW91IHNlbGVjdGVkIGBrPTZgIGZvciBhbiBhbmFseXNpcywgb3IgdGhlIFNwZWFybWFuIG92ZXIgUGVhcnNvbiBvcHRpb24gZm9yIHlvdXIgY29ycmVsYXRpb24gbWF0cml4LCBvciBxdWFudGlsZSBvdmVyIG1lZGlhbiBub3JtYWxpemF0aW9uLCBvciB3aHkgeW91IG1hZGUgdGhlIGRlY2lzaW9uIHRvIGZpbHRlciBvdXQgY2VydGFpbiBzYW1wbGVzLg0KDQotICAgQnJlYWsgeW91ciBjb2RlIGludG8gc2VjdGlvbnMgdG8gbWFrZSBpdCByZWFkYWJsZS4gU2NyaXB0cyBhcmUganVzdCBhIHNlcmllcyBvZiBzdGVwcyBhbmQgbWFqb3Igc3RlcHMgc2hvdWxkIGJlIHRpdGxlZC9vdXRsaW5lZCB3aXRoIHlvdXIgcmVhc29uaW5nIC0gbXVjaCBsaWtlIHdoZW4gcHJlc2VudGluZyB5b3VyIHJlc2VhcmNoLg0KDQotICAgR2l2ZSB5b3VyIG9iamVjdHMgaW5mb3JtYXRpdmUgb2JqZWN0IG5hbWVzICoqdGhhdCBhcmUgbm90IHRoZSBzYW1lIGFzIGZ1bmN0aW9uIG5hbWVzKiouDQoNCioqQ29tbWVudHMgbWF5L3Nob3VsZCBhcHBlYXIgaW4gdGhyZWUgcGxhY2VzOioqDQoNCi0gICBBdCB0aGUgYmVnaW5uaW5nIG9mIHlvdXIgY29kZTogV2hhdCdzIHRoZSBvYmplY3RpdmUgb2YgeW91ciBzY3JpcHQ/DQotICAgQWJvdmUgZXZlcnkgZnVuY3Rpb24geW91IGNyZWF0ZTogV2h5IGRpZCB5b3UgaGF2ZSB0byB3cml0ZSB5b3VyIG93biBmdW5jdGlvbiB2ZXJzdXMgdGhvc2UgdGhhdCBhcmUgYWxyZWFkeSBhdmFpbGFibGUgaW4gcGFja2FnZSB4Pw0KLSAgIEluLWxpbmUgb3IgaW4tYmV0d2VlbiBsaW5lcyBvZiBjb2RlOiBXaHkgZGlkIHlvdSB3cml0ZSB0aGF0IHBpZWNlIG9mIGNvZGU/IFdoYXQgZG9lcyBpdCBkbz8gV2h5IGRpZCB5b3UgY2hhbmdlIGEgZnVuY3Rpb24ncyBkZWZhdWx0cz8gXCpcKlwqDQoNCmBgYCAgICAgICAgIA0KIyBFeGFtcGxlIGNvbW1lbnRpbmcgc2VjdGlvbg0KIyBBdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBzY3JpcHQsIGRlc2NyaWJpbmcgdGhlIHB1cnBvc2Ugb2YgeW91ciBzY3JpcHQgYW5kIHdoYXQgeW91IGFyZSB0cnlpbmcgdG8gc29sdmUNCg0KYmVkbWFzQW5zd2VyIDwtIDUgKyA0ICogNiAtIDAgI0luIGxpbmU6IERlc2NyaWJpbmcgYSBwYXJ0IG9mIHlvdXIgY29kZSB0aGF0IGlzIG5vdCBvYnZpb3VzIHdoYXQgaXQgaXMgZm9yLiANCg0KIy0tLS0tLS0tLS0gU2VjdGlvbiBkaXZpZGVycyBoZWxwcyBvcmdhbml6ZSBjb2RlIHN0cnVjdHVyZSAtLS0tLS0tLS0tIw0KIyMgRmVlbCBmcmVlIHRvIGFkZCBleHRyYSBoYXNoIHRhZ3MgdG8gdmlzdWFsbHkgc2VwYXJhdGUgb3IgZW1waGFzaXplIGNvbW1lbnRzDQpgYGANCg0KKipNYWludGFpbmluZyB3ZWxsLWRvY3VtZW50ZWQgY29kZSBpcyBhbHNvIGdvb2QgZm9yIG1lbnRhbCBoZWFsdGghKioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjQuMiBOYW1pbmcgY29udmVudGlvbnMgZm9yIGZpbGVzLCBvYmplY3RzLCBhbmQgZnVuY3Rpb25zIGluIFINCg0KLSAgIENhbm5vdCBzdGFydCB3aXRoIGEgbnVtYmVyDQotICAgQ2Fubm90IGNvbnRhaW4gc3BhY2VzIG9yIHNwZWNpYWwgY2hhcmFjdGVycyBpbiB0aGUgbmFtZQ0KLSAgIEF2b2lkIG5hbWluZyB5b3VyIHZhcmlhYmxlcyB1c2luZyBuYW1lcyBhbHJlYWR5IHVzZWQgYnkgUiAoZm9yLCBuZXh0LCB3aGlsZSwgZXRjLikuDQotICAgQ29uc2lkZXIgYXBwZW5kaW5nIHRoZSBvYmplY3QgdHlwZSB0byB5b3VyIHZhcmlhYmxlIG5hbWUgKGRhdGEgZnJhbWUgPSBkZiwgbGlzdCA9IGxpc3QsIGV0Yy4pDQoNCioqU3R5bGlzdGljYWxseSwgeW91IGhhdmUgdGhlIGZvbGxvd2luZyBvcHRpb25zOioqDQoNCi0gICBBbGwgbG93ZXIgY2FzZTogZS5nLiAqKm15Zmlyc3RvYmplY3QqKg0KLSAgIFBlcmlvZCBzZXBhcmF0ZWQ6IGUuZy4gKipteS5maXJzdC5vYmplY3QqKg0KLSAgIFVuZGVyc2NvcmUgc2VwYXJhdGVkOiBlLmcuICoqbXlfZmlyc3Rfb2JqZWN0KioNCi0gICBjYW1lbENhc2UxOiBlLmcuICoqbXlGaXJzdE9iamVjdCoqDQotICAgQ2FtZWxDYXNlMjogZS5nLiAqKk15Rmlyc3RPYmplY3QqKiAoVXN1YWxseSByZXNlcnZlZCBmb3IgKkNsYXNzKiBuYW1lcykNCg0KVGhlIG1vc3QgaW1wb3J0YW50IGFzcGVjdHMgb2YgbmFtaW5nIGNvbnZlbnRpb25zIGFyZSBiZWluZyBjb25jaXNlIGFuZCBjb25zaXN0ZW50ISBUaHJvdWdob3V0IHRoaXMgY291cnNlIHlvdSdsbCBzZWUgYSBoeWJyaWQgc3lzdGVtIHRoYXQgdXNlcyB0aGUgKip1bmRlcnNjb3JlKiogdG8gc2VwYXJhdGUgd29yZHMgYnV0IGEgKipwZXJpb2QqKiByaWdodCBiZWZvcmUgZGVub3RpbmcgdGhlIG9iamVjdCB0eXBlIGllIGB0aGlzX2RhdGEub2JqZWN0YC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjQuMyBCZXN0IFByYWN0aWNlcyBmb3IgV3JpdGluZyBTY3JpcHRzDQoNCi0gICBTdGFydCBlYWNoIHNjcmlwdCB3aXRoIGEgZGVzY3JpcHRpb24gb2Ygd2hhdCBpdCBkb2VzLg0KDQotICAgVGhlbiBsb2FkIGFsbCByZXF1aXJlZCBwYWNrYWdlcy4NCg0KLSAgIENvbnNpZGVyIHdoYXQgd29ya2luZyBkaXJlY3RvcnkgeW91IGFyZSBpbiB3aGVuIHNvdXJjaW5nIGEgc2NyaXB0Lg0KDQotICAgVXNlIGNvbW1lbnRzIHRvIG1hcmsgb2ZmIHNlY3Rpb25zIG9mIGNvZGUuDQoNCi0gICBQdXQgZnVuY3Rpb24gZGVmaW5pdGlvbnMgYXQgdGhlIHRvcCBvZiB5b3VyIGZpbGUsIG9yIGluIGEgc2VwYXJhdGUgZmlsZSBpZiB0aGVyZSBhcmUgbWFueS4NCg0KLSAgIE5hbWUgYW5kIHN0eWxlIGNvZGUgY29uc2lzdGVudGx5Lg0KDQotICAgQnJlYWsgY29kZSBpbnRvIHNtYWxsLCBkaXNjcmV0ZSBwaWVjZXMuDQoNCiAgICAtICAgVGhpcyBpcyBtb3JlIGVhc2lseSBhY2NvbXBsaXNoZWQgd2hlbiB3b3JraW5nIHdpdGggZGlmZmVyZW50IGNvZGUgY2VsbHMgaW4gSnVweXRlciBOb3RlYm9vay4NCg0KLSAgIEZhY3RvciBvdXQgY29tbW9uIG9wZXJhdGlvbnMgcmF0aGVyIHRoYW4gcmVwZWF0aW5nIHRoZW0uDQoNCi0gICBLZWVwIGFsbCBvZiB0aGUgc291cmNlIGZpbGVzIGZvciBhIHByb2plY3QgaW4gb25lIGRpcmVjdG9yeSBhbmQgdXNlIHJlbGF0aXZlIHBhdGhzIHRvIGFjY2VzcyB0aGVtLg0KDQogICAgLSAgIFVzaW5nIHJlbGF0aXZlIHBhdGhzIGNsb3NlIG9yIGluc2lkZSB5b3VyIHNjcmlwdCdzIGRpcmVjdG9yIG1ha2VzIGl0IGVhc2llciB0byBwYWNrYWdlIG9yIG1vdmUgYXJvdW5kIHlvdXIgc2NyaXB0cyB0b28uDQoNCi0gICBLZWVwIHRyYWNrIG9mIHRoZSBtZW1vcnkgdXNlZCBieSB5b3VyIHByb2dyYW0uDQoNCi0gICBBbHdheXMgc3RhcnQgd2l0aCBhIGNsZWFuIGVudmlyb25tZW50IGluc3RlYWQgb2Ygc2F2aW5nIHRoZSB3b3Jrc3BhY2UuDQoNCi0gICBLZWVwIHRyYWNrIG9mIHNlc3Npb24gaW5mb3JtYXRpb24gaW4geW91ciBwcm9qZWN0IGZvbGRlci4NCg0KLSAgIEhhdmUgc29tZW9uZSBlbHNlIHJldmlldyB5b3VyIGNvZGUuDQoNCi0gICBVc2UgdmVyc2lvbiBjb250cm9sLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBiZXN0IGNvZGluZyBwcmFjdGljZXMsIHBsZWFzZSB2aXNpdCBbc3djYXJwZW50cnldKGh0dHBzOi8vc3djYXJwZW50cnkuZ2l0aHViLmlvL3Itbm92aWNlLWluZmxhbW1hdGlvbi8wNi1iZXN0LXByYWN0aWNlcy1SLykNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuNS4wIFRyb3VibGUtc2hvb3RpbmcgYmFzaWNzDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovR29vZ2xpbmdGb3JQcm9ncmFtbWVycy5qcGc/cmF3PXRydWUiIHdpZHRoPSI2MDAiLz4NCg0KV2UgYWxsIHJ1biBpbnRvIHByb2JsZW1zLiBXZSdsbCBzZWUgYSBsb3Qgb2YgbWlzdGFrZXMgaGFwcGVuIGluIGNsYXNzIHRvbyEgVGhhdCdzIE9LIGlmIHdlIGNhbiBsZWFybiBmcm9tIG91ciBlcnJvcnMgYW5kIHF1aWNrbHkgKG9yIGV2ZW50dWFsbHkpIHJlY292ZXIuDQo6OjoNCg0KIyMjIDEuNS4xIERldGVybWluZSB0aGUgbG9jYXRpb24gYW5kIHR5cGUgb2YgZXJyb3INCg0KVXN1YWxseSB3aGVuIFIgZ2VuZXJhdGVzIGFuIGVycm9yIGl0IHdpbGwgcHJvZHVjZSBzb21lIGluZm9ybWF0aW9uIGFib3V0IHdoYXQgaGFzIGhhcHBlbmVkLiBUaGlzIHVzdWFsbHkgaW5jbHVkZXMgYW4gZXJyb3IgbWVzc2FnZSBkZXRhaWxpbmcgdGhlICpraW5kIG9mIGVycm9yKiBpdCBlbmNvdW50ZXJlZCBvciBhbiBlcnJvciBtZXNzYWdlIGdlbmVyYXRlZCBieSB0aGUgZnVuY3Rpb24uIEl0IGNhbiBhbHNvIGluY2x1ZGUgYSBsaW5lIHdoZXJlIHRoZSBlcnJvciB3YXMgZW5jb3VudGVyZWQsIG9yIHRoZSBuYW1lIG9mIHRoZSBsYXN0IGZ1bmN0aW9uIHRoYXQgd2FzIGNhbGxlZCBiZWZvcmUgdGhlIGVycm9yIHdhcyBlbmNvdW50ZXJlZC4NCg0KIyMjIDEuNS4yIENvbW1vbiBlcnJvcnMNCg0KLSAgICpmaWxlIGRvZXMgbm90IGV4aXN0KjogVXNlIGBnZXR3ZCgpYCB0byBjaGVjayB3aGVyZSB5b3UgYXJlIHdvcmtpbmcsIHR5cGVgbGlzdC5maWxlcygpYCBvciB0aGUgYEZpbGVzYCBwYW5lIHRvIGNoZWNrIHRoYXQgeW91ciBmaWxlIGV4aXN0cyB0aGVyZSwgYW5kIGBzZXR3ZCgpYCB0byBjaGFuZ2UgeW91ciBkaXJlY3RvcnkgaWYgbmVjZXNzYXJ5LiBQcmVmZXJhYmx5LCB3b3JrIGluc2lkZSBhbiAqUiBwcm9qZWN0KiB3aXRoIGFsbCBwcm9qZWN0LXJlbGF0ZWQgZmlsZXMgaW4gdGhhdCBzYW1lIGZvbGRlci4gWW91ciB3b3JraW5nIGRpcmVjdG9yeSB3aWxsIGJlIHNldCBhdXRvbWF0aWNhbGx5IHdoZW4geW91IG9wZW4gdGhlIHByb2plY3QgKHRoaXMgY2FuIGJlIGRvbmUgYnkgdXNpbmcgYEZpbGUgLT4gTmV3IE5vdGVib29rYCBhbmQgZm9sbG93aW5nIHByb21wdHMpLg0KDQotICAgKnR5cG9zKjogUiBpcyBjYXNlIHNlbnNpdGl2ZSBzbyBhbHdheXMgY2hlY2sgdGhhdCB5b3UndmUgc3BlbGxlZCBldmVyeXRoaW5nIHJpZ2h0LiBHZXQgdXNlZCB0byB1c2luZyB0aGUgKnRhYi1hdXRvY29tcGxldGlvbiogZmVhdHVyZSB3aGVuIHBvc3NpYmxlLiBUaGlzIGNhbiByZWR1Y2UgdHlwb3MgYW5kIGluY3JlYXNlIHlvdXIgb3ZlcmFsbCBwcm9ncmFtbWluZyBzcGVlZC4NCg0KLSAgICpvcGVuIHF1b3RlcywgcGFyZW50aGVzZXMsIGJyYWNrZXRzKjoNCg0KICAgIC0gICAqKkp1cHl0ZXIgTm90ZWJvb2tzKiogaGlnaGxpZ2h0IHRoZSBjdXJyZW50IGN1cnNvci1kZW5vdGVkIGJyYWNrZXQgc2V0IGluICRcY29sb3J7Z3JlZW59e1x0ZXh0e2dyZWVufX0kLiBJZiB0aGUgYnJhY2tldCBpcyB1bm1hdGNoZWQgb24gZWl0aGVyIHNpZGUsIGl0IHdpbGwgYmUgJFxjb2xvcntyZWR9e1x0ZXh0e3JlZH19JC4NCg0KLSAgICpkYXRhIHR5cGUqOiBVc2UgY29tbWFuZHMgbGlrZSBgdHlwZW9mKClgIGFuZCBgY2xhc3MoKWAgdG8gY2hlY2sgd2hhdCB0eXBlIG9mIGRhdGEgeW91IGhhdmUuIFVzZSBgc3RyKClgIHRvIHBlYWsgYXQgeW91ciBkYXRhIHN0cnVjdHVyZXMgaWYgeW91J3JlIG1ha2luZyBhc3N1bXB0aW9ucyBhYm91dCBpdC4NCg0KLSAgICp1bmV4cGVjdGVkIGFuc3dlcnMqOiBUbyBhY2Nlc3MgdGhlICpoZWxwIG1lbnUqLCB0eXBlIGBoZWxwKCJmdW5jdGlvbiIpYCwgYD9mdW5jdGlvbmAgKHVzaW5nIHRoZSBuYW1lIG9mIHRoZSBmdW5jdGlvbiB0aGF0IHlvdSB3YW50IHRvIGNoZWNrKSwgb3IgYGhlbHAocGFja2FnZSA9ICJwYWNrYWdlX25hbWUiKWAuDQoNCiAgICAtICAgKipKdXB5dGVyIE5vdGVib29rcyoqOiB0aGUgcmVzdWx0IHdpbGwgY29tZSBmcm9tIGEgcG9wLXVwIHdpbmRvdyBvbiB0aGUgYm90dG9tIG9mIHRoZSBub3RlYm9vay4NCg0KLSAgICpmdW5jdGlvbiBub3QgZm91bmQqOiBNYWtlIHN1cmUgdGhlIHBhY2thZ2UgbmFtZSBpcyBwcm9wZXJseSBzcGVsbGVkLCBpbnN0YWxsZWQsIEFORCBsb2FkZWQuIExpYnJhcmllcyBjYW4gYmUgbG9hZGVkIHRvIHRoZSBlbnZpcm9ubWVudCB1c2luZyB0aGUgZnVuY3Rpb24gYGxpYnJhcnkoInBhY2thZ2VfbmFtZSIpYC4gSWYgeW91IG9ubHkgbmVlZCBvbmUgZnVuY3Rpb24gZnJvbSBhIHBhY2thZ2UsIG9yIG5lZWQgdG8gc3BlY2lmeSB0byB3aGF0IHBhY2thZ2UgYSBmdW5jdGlvbiBiZWxvbmdzIGJlY2F1c2UgdGhlcmUgYXJlIGZ1bmN0aW9ucyB3aXRoIHRoZSBzYW1lIG5hbWUgdGhhdCBiZWxvbmcgdG8gZGlmZmVyZW50IHBhY2thZ2VzLCB5b3UgY2FuIHVzZSBhIGRvdWJsZSBjb2xvbiwgaS5lLiBgcGFja2FnZV9uYW1lOjpmdW5jdGlvbl9uYW1lYC4NCg0KLSAgICp0aGUgUiBib21iISEqOiBUaGUgYHNlc3Npb24gYWJvcnRlZGAgY2FuIGhhcHBlbiBmb3IgYSB2YXJpZXR5IG9mIHJlYXNvbnMsIGxpa2Ugbm90IGhhdmluZyBlbm91Z2ggY29tcHV0YXRpb25hbCBwb3dlciB0byBwZXJmb3JtIGEgdGFzayBvciBhbHNvIGJlY2F1c2Ugb2YgYSBzeXN0ZW0td2lkZSBmYWlsdXJlLg0KDQogICAgLSAgICoqSnVweXRlciBOb3RlYm9va3MqKjogcmVzdGFydCB0aGUgS2VybmVsIGZyb20gdGhlIG1lbnUgb3IgYnkgZG91YmxlLXRhcHBpbmcgYDBgLiBZb3Ugd2lsbCBuZWVkIHRvIHJlcnVuIHlvdXIgcHJldmlvdXMgY2VsbHMhDQoNCi0gICAqKmNoZWF0c2hlZXRzKio6IE1lZXQgeW91ciBuZXcgYmVzdCBmcmllbmRzOiBbY2hlYXRzaGVldHNdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykhDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovV2lsZGUtcXVvdGUuanBnP3Jhdz10cnVlIiB3aWR0aD0iNzAwIi8+DQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjUuMyBGaW5kaW5nIGFuc3dlcnMgb25saW5lDQoNCi0gICA5OSUgb2YgdGhlIHRpbWUsIHNvbWVvbmUgaGFzIGFscmVhZHkgYXNrZWQgeW91ciBxdWVzdGlvblwNCg0KLSAgIEdvb2dsZSwgU3RhY2sgb3ZlcmZsb3csIFIgQmxvZ2dlcnMsIFNFUWFuc3dlcnMsIFF1b3JhLCBSZXNlYXJjaEdhdGUsIFJTZWVrLCB0d2l0dGVyLCBldmVuIHJlZGRpdFwNCg0KLSAgIEluY2x1ZGluZyB0aGUgcHJvZ3JhbSwgdmVyc2lvbiwgZXJyb3IsIHBhY2thZ2UgYW5kIGZ1bmN0aW9uIGhlbHBzLCBzbyBiZSBzcGVjaWZpYy4gU29tZXRpbWVzIGl0IGlzIHVzZWZ1bCB0byBpbmNsdWRlIHlvdXIgb3BlcmF0aW5nIHN5c3RlbSBhbmQgdmVyc2lvbiAoV2luZG93cyAxMCwgVWJ1bnR1IDE4LCBNYWMgT1MgMTAsIGV0Yy4pLg0KDQotICAgWW91IG1heSBydW4gaW50byBhc3NpZ25tZW50IHF1ZXN0aW9ucyB3aGVyZSB0aGUgdG9vbHMgSSd2ZSBwcm92aWRlZCBpbiBsZWN0dXJlIGFyZSBub3QgZW5vdWdoIHRvIHJlcHJvZHVjZSB0aGUgZXhhbXBsZSBvdXRwdXQgKmV4YWN0bHkqIGFzIHByb3ZpZGVkLiBJZiB5b3Ugd2lzaCB0byBnbyB0aGF0IGV4dHJhIG1pbGUgeW91IG1heSBuZWVkIHRvIGxvb2sgZm9yIGFuc3dlcnMgZWxzZXdoZXJlIGJ5IGNvbnN1bHRpbmcgcmVmZXJlbmNlcyBmcm9tIHRoZSBjbGFzcyBvciBzZWFyY2hpbmcgZm9yIGl0IHlvdXJzZWxmLiAqKipUaGUgdHJ1dGggaXMgb3V0IHRoZXJlISoqKg0KDQojIyMgMS41LjMuMSBBc2tpbmcgYSBxdWVzdGlvbiBpbiBhbiBvbmxpbmUgZm9ydW0NCg0KLSAgIFN1bW1hcml6ZSB5b3VyIHF1ZXN0aW9uIGluIHRoZSB0aXRsZSAoYmUgY29uY2lzZSBhbmQgb2JqZWN0aXZlISkuDQotICAgSW50cm9kdWNlIHlvdXIgcXVlc3Rpb24sIGhvdyB5b3UgcmFuIGludG8gdGhlIHByb2JsZW0sICoqYW5kIGhvdyB5b3UgdHJpZWQgdG8gc29sdmUgaXQgeW91cnNlbGYqKi4gSWYgeW91IGhhdmVuJ3QgZG9uZSB0aGUgYm9sZGVkIHRoaW5nLCBkbyB0aGUgYm9sZGVkIHRoaW5nLg0KLSAgIFNob3cgZW5vdWdoIG9mIHlvdXIgY29kZSBhbmQgZGF0YSBmb3Igb3RoZXJzIHRvIHRyeSB0byByZXByb2R1Y2UgdGhlIHByb2JsZW0vZXJyb3IuDQotICAgQWRkIHRhZ3MgdGhhdCBtYXRjaCB5b3VyIHByb2JsZW0uDQotICAgUmVzcG9uZCB0byB0aGUgZmVlZGJhY2sgYW5kIHZvdGUgZm9yIHRoZSBhbnN3ZXJlZCB0aGF0IHlvdSBwaWNrZWQuIFBlb3BsZSBwdXQgaW4gdGhlaXIgZnJlZSB0aW1lIHRvIGFuc3dlciBhbmQgaGVscCB5b3UuDQotICAgVGFrZSBhIGxvb2sgYXQgW1N0YWNrT3ZlcmZsb3cncyB0aXBzIG9uIGhvdyB0byBhc2sgcXVlc3Rpb25zXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2hlbHAvaG93LXRvLWFzayksIGFzIHdlbGwgYXMgW0NSQU4nc10oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy9wb3N0aW5nLWd1aWRlLmh0bWwpDQoNCioqUmVtZW1iZXIqKjogRXZlcnlvbmUgbG9va3MgZm9yIGhlbHAgb25saW5lICoqQUxMIFRIRSBUSU1FKiouIEl0IGlzIHZlcnkgY29tbW9uLiBBbHNvLCB3aXRoIHByb2dyYW1taW5nIHRoZXJlIGFyZSBtdWx0aXBsZSB3YXlzIHRvIGNvbWUgdXAgd2l0aCBhbiBhbnN3ZXIsIGV2ZW4gZGlmZmVyZW50IHBhY2thZ2VzIHRoYXQgbGV0IHlvdSBkbyB0aGUgc2FtZSB0aGluZyBpbiBkaWZmZXJlbnQgd2F5cy4gWW91IHdpbGwgd29yayBvbiByZWZpbmluZyB0aGVzZSBhc3BlY3RzIG9mIHlvdXIgY29kZSBhcyB5b3UgZ28gYWxvbmcgaW4gdGhpcyBjb3Vyc2UgYW5kIGluIHlvdXIgY29kaW5nIGNhcmVlci4NCg0KTGFzdCBidXQgbm90IGxlYXN0LCB0byBtYWtlIGxpZmUgZWFzaWVyOiBVbmRlciB0aGUgYEhlbHBgIHBhbmUsIHRoZXJlIGlzIGEgY2hlYXRzaGVldCBvZiAqSnVweXRlciBub3RlYm9vayBrZXlib2FyZCBzaG9ydGN1dHMqIG9yIGEgYnJvd3NlciBsaXN0IFtoZXJlXShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vanlweXRlci1ub3RlYm9vay1zaG9ydGN1dHMtYmYwMTAxYTk4MzMwKS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovYmFja1RvVGhlRm91bmRhdGlvbnNPZlItbG9nby1wbmctdHJhbnNwYXJlbnQucG5nP3Jhdz10cnVlIiB3aWR0aD0iNzAwIi8+DQo6OjoNCg0KIyAyLjAuMCBGb3VuZGF0aW9ucyBvZiBSDQoNClRoZXJlIGFyZSBtYW55IHRpcHMgYW5kIHRyaWNrcyB0byByZW1lbWJlciBhYm91dCBSIGJ1dCBoZXJlIHdlJ2xsIHF1aWNrbHkgcmVjYWxsIHNvbWUgZm91bmRhdGlvbmFsIGtub3dsZWRnZSB0aGF0IGNvdWxkIGJlIHJlbGV2YW50IGluIGxhdGVyIGxlY3R1cmVzLg0KDQojIyAyLjEuMCBBc3NpZ25pbmcgdmFyaWFibGVzDQoNCklmIHdlIHdhbnQgdG8gaG9sZCBvbiB0byBhIG51bWJlciwgY2FsY3VsYXRpb24sIG9yIG9iamVjdCB3ZSBuZWVkIHRvICphc3NpZ24qIGl0IHRvIGEgbmFtZWQgdmFyaWFibGUuIFIgaGFzIG11bHRpcGxlIG1ldGhvZHMgZm9yIGFzc2lnbmluZyBhIHZhbHVlIHRvIGEgdmFyaWFibGUgYW5kIGFuIG9yZGVyIG9mIHByZWNlZGVuY2UhDQoNCmAtPmAgYW5kIGAtPj5gICoqUmlnaHR3YXJkIGFzc2lnbm1lbnQqKjogd2Ugd29uJ3QgcmVhbGx5IGJlIHVzaW5nIHRoaXMgaW4gb3VyIGNvdXJzZS4NCg0KYDwtYCBhbmQgYDw8LWAgKipMZWZ0d2FyZCBhc3NpZ25tZW50Kio6IGFzc2lnbm1lbnQgdXNlZCBieSBtb3N0ICdhdXRoZW50aWMnIFIgcHJvZ3JhbW1lcnMgYnV0IHJlYWxseSBqdXN0IGEgaGlzdG9yaWNhbCBrZXlib2FyZCB0aHJvd2JhY2suDQoNCmA9YCAqKkxlZnR3YXJkIGFzc2lnbm1lbnQqKjogY29tbW9ubHkgdXNlZCB0b2tlbiBmb3IgYXNzaWdubWVudCBpbiBtYW55IG90aGVyIHByb2dyYW1taW5nIGxhbmd1YWdlcyBidXQgaG9sZHMgZHVhbCBtZWFuaW5nIQ0KDQojIyMgTm90ZXMNCg0KLSAgIEluIFIsIHRoZSBhc3NpZ25tZW50IG9mIGEgdmFyaWFibGUgKmRvZXMgbm90KiBwcm9kdWNlIGFueSBzdGFuZGFyZCBvdXRwdXQuDQoNCi0gICBSIHByb2Nlc3NlcyBhdCBlYWNoIG5ldyBsaW5lIHVubGVzcyB5b3UgdXNlIGEgc2VtaWNvbG9uICg7KSB0byBzZXBhcmF0ZSBjb21tYW5kcy4gVGhpcyBhcHBsaWVzIHRvIGFzc2lnbm1lbnQgYXMgd2VsbC4gT25lIGV4Y2VwdGlvbiBiZWluZyB3aGVuIHlvdXIgZnVuY3Rpb24gY2FsbHMgYXJlIHNwYWNlZCBhY3Jvc3MgbGluZXMgYW5kIGNvbnRhaW5lZCB3aXRoaW4gdGhlIGAoKWAuDQoNCi0gICBSIGNhbGN1bGF0ZXMgdGhlIHJpZ2h0IHNpZGUgb2YgdGhlIGFzc2lnbm1lbnQgZmlyc3QgdGhlIHJlc3VsdCBpcyB0aGVuIGFwcGxpZWQgdG8gdGhlIGxlZnQuDQoNCiAgICAtICAgVGhpcyBpcyBhIGNvbW1vbiBwYXJhZGlnbSBpbiBwcm9ncmFtbWluZyB0aGF0IHNpbXBsaWZpZXMgdmFyaWFibGUgYmVoYXZpb3VycyBmb3IgY291bnRpbmcgYW5kIHRyYWNraW5nIHJlc3VsdHMgYXMgdGhleSBidWlsZCB1cCBvdmVyIHRpbWUuIFRoaXMgYWxzbyBhbGxvd3MgdXMgdG8gaW5jcmVtZW50IHZhcmlhYmxlcyBvciBtYW5pcHVsYXRlIG9iamVjdHMgdG8gdXBkYXRlIHRoZW0hDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjIuMCBEYXRhIHR5cGVzIGFyZSB0aGUgYmFzaWMgYnVpbGRpbmcgYmxvY2tzIG9mIFINCg0KRGF0YSB0eXBlcyBhcmUgdXNlZCB0byBjbGFzc2lmeSB0aGUgYmFzaWMgc3BlY3RydW0gb2YgdmFsdWVzIHRoYXQgYXJlIHVzZWQgaW4gUi4gSGVyZSdzIGEgdGFibGUgZGVzY3JpYmluZyAqc29tZSogb2YgdGhlIGNvbW1vbiBkYXRhIHR5cGVzIHdlJ2xsIGVuY291bnRlci4NCg0KfCBEYXRhIHR5cGUgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRXhhbXBsZSAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tfA0KfCBjaGFyYWN0ZXIgfCBDYW4gYmUgc2luZ2xlIG9yIG11bHRpcGxlIGNoYXJhY3RlcnMgKHN0cmluZ3MpIG9mIGxldHRlcnMgYW5kIHN5bWJvbHMuIEFzc2lnbmVkIHVzaW5nIGRvdWJsZSBgJ2Agb3IgYCJgIHwgYSNjJkUgICAgICAgICB8DQp8IGludGVnZXIgICB8IFdob2xlIG51bWJlciB2YWx1ZXMsIGVpdGhlciBwb3NpdGl2ZSBvciBuZWdhdGl2ZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxICAgICAgICAgICAgIHwNCnwgZG91YmxlICAgIHwgQW55IG51bWJlciB0aGF0IGlzIG5vdCBhbiBpbnRlZ2VyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDcuNSAgICAgICAgICAgfA0KfCBsb2dpY2FsICAgfCBBbHNvIGtub3duIGFzIGEgYm9vbGVhbiwgcmVwcmVzZW50aW5nIHRoZSBzdGF0ZSBvZiBhIGNvbmRpdGlvbmFsIChxdWVzdGlvbikgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgVFJVRSBvciBGQUxTRSB8DQp8IE5BICAgICAgICB8IFJlcHJlc2VudHMgdGhlIHZhbHVlIG9mICJOb3QgQXZhaWxhYmxlIiB1c3VhbGx5IHNlZW4gd2hlbiBpbXBvcnRlZCBkYXRhIGhhcyBtaXNzaW5nIHZhbHVlcyAgICAgICAgICAgICAgfCBOQSAgICAgICAgICAgIHwNCg0KIyMjIDIuMi4xIERhdGEgc3RydWN0dXJlcyBob2xkIHNpbmdsZSBvciBtdWx0aXBsZSB2YWx1ZXMNCg0KVGhlIGpvYiBvZiBkYXRhIHN0cnVjdHVyZXMgaXMgdG8gImhvc3QiIHRoZSBkaWZmZXJlbnQgZGF0YSB0eXBlcy4gVGhlcmUgYXJlIGZpdmUgYmFzaWMgdHlwZXMgb2YgZGF0YSBzdHJ1Y3R1cmVzIHRoYXQgd2UnbGwgdXNlIGluIFI6DQoNCnwgRGF0YSBzdHJ1Y3R1cmUgfCBEaW1lbnNpb25zICAgICAgIHwgUmVzdHJpY3Rpb25zICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgdmVjdG9yICAgICAgICAgfCAxRCAgICAgICAgICAgICAgIHwgSG9sZHMgYSBzaW5nbGUgZGF0YSB0eXBlICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgbWF0cml4ICAgICAgICAgfCAyRCAgICAgICAgICAgICAgIHwgSG9sZHMgYSBzaW5nbGUgZGF0YSB0eXBlICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYXJyYXkgICAgICAgICAgfCBuRCAgICAgICAgICAgICAgIHwgSG9sZHMgYSBzaW5nbGUgZGF0YSB0eXBlICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZGF0YSBmcmFtZSAgICAgfCAyRCAgICAgICAgICAgICAgIHwgSG9sZHMgbXVsdGlwbGUgZGF0YSB0eXBlcyB3aXRoIHNvbWUgcmVzdHJpY3Rpb25zIHwNCnwgbGlzdCAgICAgICAgICAgfCAxRCAodGVjaG5pY2FsbHkpIHwgSG9sZHMgbXVsdGlwbGUgZGF0YSB0eXBlcyBBTkQgc3RydWN0dXJlcyAgICAgICAgIHwNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9kYXRhX3N0cnVjdHVyZXMuanBnP3Jhdz10cnVlIiB3aWR0aD0iNzAwIi8+DQoNClNvbWV0aW1lcyBpdCBpcyBoZWxwZnVsIHRvIGltYWdpbmUgRGF0YSBTdHJ1Y3R1cmVzIGFzIHJlYWwtd29ybGQgb2JqZWN0cyB0byB1bmRlcnN0YW5kIGhvdyB0aGV5IGFyZSBzaGFwZWQgYW5kIHJlbGF0ZWQgdG8gZWFjaCBvdGhlci4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMi4yIFZlY3RvcnMgYXJlIGxpa2UgYSBxdWV1ZSBvZiBhIHNpbmdsZSBkYXRhIHR5cGUNCg0KLSAgIEFsc28ga25vd24gYXMgYXRvbWljIHZlY3RvcnMsIGVhY2ggZWxlbWVudCB3aXRoaW4gYSB2ZWN0b3IgbXVzdCBiZSBvZiB0aGUgc2FtZSBkYXRhIHR5cGU6ICoqbG9naWNhbCwgaW50ZWdlciwgZG91YmxlLCBjaGFyYWN0ZXIqKiwgY29tcGxleCwgb3IgcmF3Lg0KDQotICAgRm9yIGVhY2ggdmVjdG9yIHRoZXJlIGFyZSB0d28ga2V5IHByb3BlcnRpZXMgdGhhdCBjYW4gYmUgcXVlcmllZCB3aXRoIGB0eXBlb2YoKWAgYW5kIGBsZW5ndGgoKWAuDQoNCi0gICBUaGVyZSBpcyBhIG51bWVyaWNhbCBvcmRlciB0byBhIHZlY3RvciwgbXVjaCBsaWtlIGEgcXVldWUgQU5EIHlvdSBjYW4gYWNjZXNzIGVhY2ggZWxlbWVudCAocGllY2Ugb2YgZGF0YSkgaW5kaXZpZHVhbGx5IG9yIGluIGdyb3Vwcy4gRWxlbWVudHMgYXJlIG9yZGVyZWQgZnJvbSBgMWAgdG8gYGxlbmd0aCh5b3VyX3ZlY3RvcilgIGFuZCBjYW4gYmUgYWNjZXNzZWQgd2l0aCBhbiBpbmRleGluZyBvcGVyYXRvciBgW11gDQoNCi0gICBFbGVtZW50cyBvZiBhIHZlY3RvciBtYXkgYmUgKipuYW1lZCoqLCB0byBmYWNpbGl0YXRlIHN1YnNldHRpbmcgYnkgY2hhcmFjdGVyIHZlY3RvcnMuDQoNCi0gICBFbGVtZW50cyBvZiBhIHZlY3RvciBtYXkgYmUgc3Vic2V0IGJ5IGEgbG9naWNhbCB2ZWN0b3IuDQoNCmBgYHtyLCBlcnJvcj1UUlVFfQ0KIyBCdWlsZCBhIGNoYXJhY3RlciB2ZWN0b3INCmNoYXIudmVjdG9yIDwtIGMoIkNhbmFkYSIsIC4uLiwgIkdyZWF0IEJyaXRhaW4iKQ0KY2hhci52ZWN0b3INCg0KIyBzdWJzZXQgYnkgYSBzaW5nbGUgdmFsdWUNCmNoYXIudmVjdG9yWy4uLl0NCg0KIyBzdWJzZXQgYnkgbXVsdGlwbGUgdmFsdWVzDQpjaGFyLnZlY3RvclsuLi5dDQoNCiMgc3Vic2V0IGJ5IHJlbW92aW5nIHZhbHVlcyAoY2Fubm90IGJlIG1peGVkIHdpdGggcG9zaXRpdmUgdmFsdWVzKQ0KY2hhci52ZWN0b3JbYygtMSwgLi4uKV0NCg0KIyBzdWJzZXQgd2l0aCByZXBlYXRpbmcgbXVsdGlwbGUgdmFsdWVzDQpjaGFyLnZlY3RvcltjKDEsIDIsIDMsIC4uLildDQpgYGANCg0KYGBge3J9DQojIEJ1aWxkIGEgbmFtZWQgY2hhcmFjdGVyIHZlY3RvciBieSBpbmNsdWRpbmcgdmFyaWFibGUgbmFtZXMNCmNoYXJhY3Rlci52ZWN0b3IgPC0gYyhhID0gLi4uLCBiID0gIlVuaXRlZCBTdGF0ZXMiLCBjID0gIkdyZWF0IEJyaXRhaW4iKQ0KY2hhcmFjdGVyLnZlY3Rvcg0KDQojIHN1YnNldCBieSBlbGVtZW50IG5hbWUNCmNoYXJhY3Rlci52ZWN0b3JbYygiYSIsIC4uLildDQoNCiMgc3Vic2V0IGJ5IGFuIGV4cGxpY2l0IHZlY3RvciBvZiBsb2dpY2Fscw0KY2hhcmFjdGVyLnZlY3RvcltjKC4uLildDQoNCiMgT3Igc3Vic2V0IGJ5IGFuIGltcGxpY2l0IHZlY3RvciBvZiBsb2dpY2Fscw0KY2hhcmFjdGVyLnZlY3RvcltjaGFyYWN0ZXIudmVjdG9yIC4uLl0NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMi4yLjEgQ29lcmNpb24gY2hhbmdlcyBkYXRhIGZyb20gb25lIHR5cGUgdG8gYW5vdGhlciAod2hlcmUgYXBwbGljYWJsZSkNCg0KUiB3aWxsIGltcGxpY2l0bHkgZm9yY2UgKCpjb2VyY2UqKSB5b3VyIHZlY3RvciB0byBiZSBvZiBvbmUgZGF0YSB0eXBlLiBJbiB0aGlzIGNhc2UsIHRoZSB0eXBlIHRoYXQgaXMgbW9zdCBpbmNsdXNpdmUgaXMgYSBjaGFyYWN0ZXIgdmVjdG9yLiBXaGVuIHdlIGV4cGxpY2l0bHkgY29lcmNlIGEgY2hhbmdlIGZyb20gb25lIGRhdGEgdHlwZSB0byB0aGUgbmV4dCwgaXQgaXMga25vd24gYXMgKmNhc3RpbmcqLiBZb3UgY2FuIGNhc3QgYmV0d2VlbiBjZXJ0YWluIGRhdGEgdHlwZXMgYW5kIGFsc28gb2JqZWN0IHR5cGVzLg0KDQotICAgVHlwZS1jYXN0aW5nIGV4YW1wbGVzOiBgYXMubG9naWNhbCgpYCwgYGFzLmludGVnZXIoKWAsIGBhcy5kb3VibGUoKWAsIGBhcy5udW1lcmljKClgLCBgYXMuY2hhcmFjdGVyKClgLCBhbmQgYGFzLmZhY3RvcigpYA0KDQotICAgU3RydWN0dXJlIGNhc3RpbmcgZXhhbXBsZXM6IGBhcy5kYXRhLmZyYW1lKClgLCBgYXMubGlzdCgpYCwgYW5kIGBhcy5tYXRyaXgoKWANCg0KSW1wb3J0YW50bHksIHdoZW4gY29lcmNpbmcsIHRoZSBSIGtlcm5lbCBjb252ZXJ0cyBmcm9tIG1vcmUgc3BlY2lmaWMgdG8gZ2VuZXJhbCB0eXBlcyB1c3VhbGx5IGluIHRoaXMgb3JkZXI6DQoNCjxicj4NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCmxvZ2ljYWwgJFxyaWdodGFycm93JCBpbnRlZ2VyICRccmlnaHRhcnJvdyQgbnVtZXJpYyAkXHJpZ2h0YXJyb3ckIGNvbXBsZXggJFxyaWdodGFycm93JCBjaGFyYWN0ZXIgJFxyaWdodGFycm93JCBsaXN0Lg0KOjo6DQoNCmBgYHtyfQ0KIyBNYWtlIGEgbG9naWNhbCB2ZWN0b3IgYW5kIGRpc3BsYXkgaXRzIHN0cnVjdHVyZQ0KbG9naWNhbC52ZWN0b3IgPC0gYyhUUlVFLCBGQUxTRSwgVFJVRSwgRkFMU0UsIEZBTFNFKQ0Kc3RyKGxvZ2ljYWwudmVjdG9yKQ0KDQojIE1ha2UgYSBudW1lcmljIHZlY3RvciBhbmQgZGlzcGxheSBpdHMgc3RydWN0dXJlDQpudW1lcmljLnZlY3RvciA8LSBjKC0xOjEwKQ0Kc3RyKG51bWVyaWMudmVjdG9yKQ0KDQojIE1ha2UgYSBtaXhlZCB2ZWN0b3IgYW5kIGRpc3BsYXkgaXRzIHN0cnVjdHVyZS4gVGFrZSBhIG5vdGUgb2YgaXRzIHR5cGluZyBhZnRlcndhcmRzDQptaXhlZC52ZWN0b3IgPC0gYyhGQUxTRSwgVFJVRSwgMSwgMiwgInRocmVlIiwgNCwgNSwgLi4uKQ0Kc3RyKG1peGVkLnZlY3RvcikNCmBgYA0KDQpgYGB7cn0NCiMgQXR0ZW1wdCB0byBjb2VyY2Ugb3VyIHZlY3RvcnMNCiMgbG9naWNhbCB0byBudW1lcmljDQphcy5udW1lcmljKC4uLikNCg0KIyBudW1lcmljIHRvIGxvZ2ljYWwNCmFzLmxvZ2ljYWwoLi4uKQ0KDQojIG51bWVyaWMgdG8gY2hhcmFjdGVyDQphcy5jaGFyYWN0ZXIoLi4uKQ0KDQojIG1peGVkIHRvIGEgbnVtZXJpYy4gTm90ZSB3aGF0IGhhcHBlbnMgd2hlbiBlbGVtZW50cyBjYW5ub3QgYmUgY29udmVydGVkDQphcy5udW1lcmljKC4uLikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuMi4zIERhdGEgRnJhbWVzIGhvbGQgdGFidWxhciBkYXRhDQoNCiMjIyAyLjIuMy4xIE9iamVjdCBjbGFzc2VzDQoNCk5vdyB0aGF0IHdlIGhhdmUgaGFkIHRoZSBvcHBvcnR1bml0eSB0byBjcmVhdGUgYSBmZXcgZGlmZmVyZW50IHZlY3RvciBvYmplY3RzLCBsZXQncyB0YWxrIGFib3V0IHdoYXQgYW4gb2JqZWN0ICpjbGFzcyogaXMuIEFuIG9iamVjdCBjbGFzcyBjYW4gYmUgdGhvdWdodCBvZiBhcyBhIHN0cnVjdHVyZSB3aXRoIGF0dHJpYnV0ZXMgdGhhdCB3aWxsIGJlaGF2ZSBhIGNlcnRhaW4gd2F5IHdoZW4gcGFzc2VkIHRvIGEgZnVuY3Rpb24uIEJlY2F1c2Ugb2YgdGhpcw0KDQotICAgZGF0YSBmcmFtZXMsIGxpc3RzIGFuZCBtYXRyaWNlcyBoYXZlIHRoZWlyIG93biBjbGFzc2VzDQotICAgdmVjdG9ycyBpbmhlcml0IGZyb20gdGhlaXIgZGF0YSB0eXBlICgqKmUuZy4qKiB2ZWN0b3JzIG9mIGNoYXJhY3RlcnMgYmVoYXZlIGxpa2UgY2hhcmFjdGVycykNCg0KU29tZSBSIHBhY2thZ2UgZGV2ZWxvcGVycyBoYXZlIGNyZWF0ZWQgdGhlaXIgb3duIG9iamVjdCBjbGFzc2VzLiBGb3IgZXhhbXBsZSwgbWFueSBvZiB0aGUgZnVuY3Rpb25zIGluIHRoZSBgdGlkeXZlcnNlYCBnZW5lcmF0ZSBgdGliYmxlYCBvYmplY3RzLiBUaGV5IGJlaGF2ZSBpbiBtb3N0IHdheXMgbGlrZSBhIGBkYXRhLmZyYW1lYCBidXQgaGF2ZSBhIG1vcmUgcmVmaW5lZCBwcmludCBzdHJ1Y3R1cmUsIG1ha2luZyBpdCBlYXNpZXIgdG8gc2VlIGluZm9ybWF0aW9uIHN1Y2ggYXMgY29sdW1uIHR5cGVzIHdoZW4gdmlld2luZyB0aGVtIHF1aWNrbHkuIEluIGdlbmVyYWwsIGZyb20gYSB0cm91YmxlLXNob290aW5nIHN0YW5kcG9pbnQsIGl0IGlzIGdvb2QgdG8gYmUgYXdhcmUgdGhhdCB5b3VyIGRhdGEgKm1heSogbmVlZCB0byBiZSBmb3JtYXR0ZWQgdG8gZml0IGEgY2VydGFpbiBjbGFzcyBvZiBvYmplY3Qgd2hlbiB1c2luZyBkaWZmZXJlbnQgcGFja2FnZXMuDQoNCkFmdGVyIHdlIGFyZSBkb25lIHRpZHlpbmcgbW9zdCBvZiBvdXIgZGF0YXNldHMsIHRoZXkgd2lsbCBiZSBpbiB0aWJibGUgb2JqZWN0cywgYnV0IGFsbCBvZiB0aGUgYmFzaWMgZGF0YSBmcmFtZSBmdW5jdGlvbnMgYXBwbHkgdG8gdGhlc2UgYXMgd2VsbC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMy4yIERhdGEgZnJhbWVzIGFyZSBncm91cHMgb2YgdmVjdG9ycyBhbGlnbmVkIGFzIGNvbHVtbnMNCg0KV2hpbGUgbWF0cmljZXMgYXJlIDItZGltZW5zaW9uYWwgc3RydWN0dXJlcyBsaW1pdGVkIHRvIGEgc2luZ2xlIHNwZWNpZmljIHR5cGUgb2YgZGF0YSB3aXRoaW4gZWFjaCBpbnN0YW5jZSwgZGF0YSBmcmFtZXMgdHJlYXQgZWFjaCBjb2x1bW4gb2YgdGhlIHN0cnVjdHVyZSBsaWtlIGEgdmVjdG9yLiBUaGUgZGF0YSBmcmFtZSwgaG93ZXZlciwgY2FuIGhhdmUgbXVsdGlwbGUgZGF0YSB0eXBlcyBtaXhlZCBhY3Jvc3MgZWFjaCBkaWZmZXJlbnQgY29sdW1uLiBEYXRhIGZyYW1lIHJ1bGVzIHRvIHJlbWVtYmVyIGFyZToNCg0KMS4gIFdpdGhpbiBhIGNvbHVtbiwgYWxsIG1lbWJlcnMgbXVzdCBiZSBvZiB0aGUgc2FtZSBkYXRhIHR5cGUgKGllIGNoYXJhY3RlciwgbnVtZXJpYywgRmFjdG9yLCBldGMuKQ0KMi4gIEFsbCBjb2x1bW5zIG11c3QgaGF2ZSB0aGUgc2FtZSBudW1iZXIgb2Ygcm93cyAoaGVuY2UgdGhlIG1hdHJpeCBzaGFwZSkNCg0KRGF0YSBmcmFtZXMgYWxsb3dzIHVzIHRvIGdlbmVyYXRlIHRhYmxlcyBvZiBtaXhlZCBpbmZvcm1hdGlvbiBtdWNoIGxpa2UgYW4gRXhjZWwgc3ByZWFkc2hlZXQuDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBkaWZmZXJlbnQgdmFyaWFibGUvY29sdW1uIHR5cGVzDQptaXhlZC5kZiA8LSBkYXRhLmZyYW1lKC4uLiA9IGNoYXJhY3Rlci52ZWN0b3IsDQogICAgICAgICAgICAgICAgICAgICAgIC4uLiA9IG51bWVyaWMudmVjdG9yWzI6NF0sDQogICAgICAgICAgICAgICAgICAgICAgIC4uLiA9IGxvZ2ljYWwudmVjdG9yWzE6M10pDQoNCiMgVmlldyB0aGUgZGF0YSBmcmFtZQ0KbWl4ZWQuZGYNCg0KIyBDaGVjayB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIGZyYW1lDQpzdHIobWl4ZWQuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMy4zIFNvbWUgdXNlZnVsIGRhdGEgZnJhbWUgY29tbWFuZHMgKGZvciBub3cpDQoNCi0gICBgbnJvdyhkYXRhX2ZyYW1lKWAgcmV0cmlldmVzIHRoZSBudW1iZXIgb2Ygcm93cyBpbiBhIGRhdGEgZnJhbWUuDQoNCi0gICBgbmNvbChkYXRhX2ZyYW1lKWAgcmV0cmlldmVzIHRoZSBudW1iZXIgb2YgY29sdW1ucyBpbiBhIGRhdGEgZnJhbWUuDQoNCi0gICBgZGF0YV9mcmFtZSRjb2x1bW5fbmFtZWAgYWNjZXNzZXMgYSBzcGVjaWZpYyBjb2x1bW4gYnkgaXQncyBuYW1lLg0KDQotICAgYGRhdGFfZnJhbWVbeCx5XWAgYWNjZXNzZXMgYSBzcGVjaWZpYyBlbGVtZW50IGxvY2F0ZWQgYXQgcm93IHgsIGNvbHVtbiB5DQoNCi0gICBgcm93bmFtZXMoZGF0YV9mcmFtZSlgIHJldHJpZXZlcyBvciBhc3NpZ25zIHJvdyBuYW1lcyB0byB5b3VyIGRhdGEgZnJhbWUNCg0KLSAgIGBjb2xuYW1lcyhkYXRhX2ZyYW1lKWAgcmV0cmlldmVzIG9yIGFzc2lnbnMgY29sdW1ucyBuYW1lcyB0byB5b3VyIGRhdGEgZnJhbWUNCg0KVGhlcmUgYXJlIG1hbnkgbW9yZSB3YXlzIHRvIGFjY2VzcyBhbmQgbWFuaXB1bGF0ZSBkYXRhIGZyYW1lcyB0aGF0IHdlJ2xsIGV4cGxvcmUgZnVydGhlciBkb3duIHRoZSByb2FkLiBMZXQncyByZXZpZXcgc29tZSBiYXNpYyBkYXRhIGZyYW1lIGNvZGUuDQoNCmBgYHtyfQ0KIyBxdWVyeSB0aGUgZGltZW5zaW9ucyBvZiB0aGUgZGF0YSBmcmFtZQ0KZGltKG1peGVkLmRmKQ0KbnJvdyhtaXhlZC5kZikNCm5jb2wobWl4ZWQuZGYpDQoNCiMgcmV0cmlldmUgcm93IGFuZCBjb2x1bW4gbmFtZXMNCnJvd25hbWVzKG1peGVkLmRmKQ0KY29sbmFtZXMobWl4ZWQuZGYpDQpgYGANCg0KYGBge3J9DQojIHByaW50IHRoZSBtaXhlZCBkYXRhIGZyYW1lDQptaXhlZC5kZg0KDQojIEFjY2VzcyBwb3J0aW9ucyBvZiB0aGUgZGF0YSBmcmFtZQ0KIyBhIHNpbmdsZSBjb2x1bW4NCnN0cihtaXhlZC5kZiQuLi4pDQoNCiMgYSBzaW5nbGUgZWxlbWVudA0KbWl4ZWQuZGZbMiwgM10NCm1peGVkLmRmWzMsIC4uLl0NCg0KIyBtdWx0aXBsZSByb3dzDQptaXhlZC5kZltjKDEsMyksIF0NCm1peGVkLmRmWy0yLCBdDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuNCBMaXN0cyBhcmUgYW1vcnBob3VzIGJ1bmRsZXMgc3RydW5nIHRvZ2V0aGVyIHdpdGggY29kZQ0KDQpMaXN0cyBjYW4gaG9sZCBtaXhlZCBkYXRhIHR5cGVzIG9mIGRpZmZlcmVudCBsZW5ndGhzLiBUaGVzZSBhcmUgZXNwZWNpYWxseSB1c2VmdWwgZm9yIGJ1bmRsaW5nIGRhdGEgb2YgZGlmZmVyZW50IHR5cGVzIHRvIHBhc3MgYXJvdW5kIHlvdXIgc2NyaXB0cywgYW5kIGZ1bmN0aW9ucywgb3Igd2hlbiByZWNlaXZpbmcgb3V0cHV0IGZyb20gZnVuY3Rpb25zISBSYXRoZXIgdGhhbiBoYXZpbmcgdG8gY2FsbCBtdWx0aXBsZSB2YXJpYWJsZXMgYnkgbmFtZSwgeW91IGNhbiBzdG9yZSB0aGVtIGluIGEgc2luZ2xlIGxpc3QhDQoNCklmIHlvdSBmb3JnZXQgdGhlIGNvbnRlbnRzIG9mIHlvdXIgbGlzdCwgdXNlIHRoZSBgc3RyKClgIGZ1bmN0aW9uIHRvIGNoZWNrIG91dCBpdHMgc3RydWN0dXJlLiBgc3RyKClgIHdpbGwgdGVsbCB5b3UgdGhlIG51bWJlciBvZiBpdGVtcyBpbiB5b3VyIGxpc3QgYW5kIHRoZWlyIGRhdGEgdHlwZXMuDQoNCmBgYHtyfQ0KIyBNYWtlIGEgbmFtZWQgbGlzdCBvZiB2YXJpb3VzIGl0ZW1zDQptaXhlZC5saXN0IDwtIGxpc3QoY291bnRyaWVzID0gY2hhcmFjdGVyLnZlY3RvciwgdmFsdWVzID0gbnVtZXJpYy52ZWN0b3IsIG1peGVkLmRhdGEgPSAuLi4pDQoNCiMgTG9vayBhdCBzb21lIGluZm9ybWF0aW9uIGFib3V0IG91ciBsaXN0DQpzdHIobWl4ZWQubGlzdCkNCg0KIyBXaGF0IGFyZSB0aGUgbmFtZXMgb2YgdGhlIGVsZW1lbnRzIGluIG1peGVkLmxpc3QNCm5hbWVzKG1peGVkLmxpc3QpDQpgYGANCg0KYGBge3J9DQojIExpc3RzIGNhbiBvZnRlbiBiZSB1bm5hbWVkDQp1bm5hbWVkLmxpc3QgPC0gbGlzdChjaGFyYWN0ZXIudmVjdG9yLCBudW1lcmljLnZlY3RvciwgLi4uKQ0KDQojIExvb2sgYXQgc29tZSBpbmZvcm1hdGlvbiBhYm91dCBvdXIgdW5uYW1lZCBsaXN0DQpzdHIodW5uYW1lZC5saXN0KQ0KbmFtZXModW5uYW1lZC5saXN0KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi4yLjQuMSBBY2Nlc3NpbmcgZWxlbWVudHMgZnJvbSBhIGxpc3QgaXMgYWNjb21wbGlzaGVkIGluIG11bHRpcGxlIHdheXMNCg0KQWNjZXNzaW5nIGxpc3RzIGlzIG11Y2ggbGlrZSBvcGVuaW5nIHVwIGEgYm94IG9mIGJveGVzIG9mIGNob2NvbGF0ZXMuIFlvdSBuZXZlciBrbm93IHdoYXQgeW91J3JlIGdvbm5hIGdldCB3aGVuIHlvdSBmb3JnZXQgdGhlIHN0cnVjdHVyZSENCg0KWW91IGNhbiBhY2Nlc3MgZWxlbWVudHMgd2l0aCBhIG1peHR1cmUgb2YgbnVtYmVyIGFuZCBuYW1pbmcgYW5ub3RhdGlvbnMgbXVjaCBsaWtlIGRhdGEgZnJhbWVzLiBBbHNvIGBbW3hdXWAgaXMgbWVhbnQgdG8gYWNjZXNzIHRoZSB4PHN1cD50aDwvc3VwPiAiZWxlbWVudCIgb2YgdGhlIGxpc3QuIE5vdGUgdGhhdCB1bm5hbWVkIGxpc3RzIGNhbm5vdCBiZSBhY2Nlc3NlZCB3aXRoIG5hbWluZyBhbm5vdGF0aW9ucy4NCg0KLSAgIGBbeF1gIHJldHVybnMgYSBsaXN0IG9iamVjdCB3aXRoIHlvdXIgZWxlbWVudChzKSBvZiBjaG9pY2UgaW4gdGhlIGxpc3QuDQotICAgYFtbeF1dYCByZXR1cm5zIGEgInNpbmdsZSIgZWxlbWVudCBvbmx5IGJ1dCB0aGF0IGVsZW1lbnQgY291bGQgYmUgYSB2ZWN0b3IsIGRhdGEgZnJhbWUsIGxpc3QsIGV0Yy4NCg0KYGBge3J9DQojIFN1YnNldCBvdXIgbGlzdCB3aXRoIFtdDQptaXhlZC5saXN0W2MoLi4uKV0NCm1peGVkLmxpc3RbLi4uXQ0KYGBgDQoNCmBgYHtyfQ0KIyBQdWxsIG91dCBhIHNpbmdsZSBlbGVtZW50DQptaXhlZC5saXN0W1syXV0NCm1peGVkLmxpc3RbWyJjb3VudHJpZXMiXV0NCg0KIyBHaXZlIGEgdmVjdG9yIGFzIGlucHV0IHRvIFtbXV0NCm1peGVkLmxpc3RbW2MoMSwzKV1dDQojIHZzIGVxdWl2YWxlbnQNCm1peGVkLmxpc3RbWzFdXVszXQ0KDQojIEFjY2VzcyBhIHNpbmdsZSBlbGVtZW50IGZyb20gYSBkYXRhIGZyYW1lIG5lc3RlZCBpbiBhIGxpc3QNCm1peGVkLmxpc3RbW2MoLi4uKV1dDQojIHZzIGVxdWl2YWxpZW50DQptaXhlZC5saXN0W1szXV1bLi4uXQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCjxiPkNvbXByZWhlbnNpb24gUXVlc3Rpb24gMi4yLjQuMTo8L2I+IFN1cHBvc2Ugd2UgaGFkIGEgbGlzdCBuYW1lZCA8Yj5tdWx0aURGLmxpc3Q8L2I+IGNvbnNpc3Rpbmcgb2YgMyBkYXRhIGZyYW1lcywgYXMgc2hvd24gaW4gdGhlIGZvbGxvd2luZyBjb2RlIGNlbGwuIEhvdyB3b3VsZCB5b3Ugc3Vic2V0IHRoZSAybmQgYW5kIDNyZCBkYXRhIGZyYW1lcyBpbnRvIHRoZWlyIG93biBsaXN0PyBIb3cgd291bGQgeW91IGFjY2VzcyB0aGUgInZhbHVlcyIgY29sdW1uIGZyb20gdGhlIDNyZCBkYXRhIGZyYW1lPyBVc2UgdGhlIGZvbGxvd2luZyBjb2RlIGNlbGwgdG8gaGVscCB5b3Ugb3V0Lg0KOjo6DQoNCmBgYHtyfQ0KbXVsdGlERi5saXN0ID0gbGlzdChtaXhlZC5kZiwgcmJpbmQobWl4ZWQuZGYsIG1peGVkLmRmKSwgcmJpbmQobWl4ZWQuZGYsIG1peGVkLmRmLCBtaXhlZC5kZikpDQoNCnN0cihtdWx0aURGLmxpc3QpDQoNCiMgU3Vic2V0IHRoZSAybmQgYW5kIDNyZCBkYXRhZnJhbWVzIGFzIHRoZWlyIG93biBsaXN0DQoNCi4uLg0KDQojIE91dHB1dCB0aGUgInZhbHVlcyIgY29sdW1uIG9mIHRoZSAzcmQgZGF0YWZyYW1lDQoNCi4uLg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjMuMCBGYWN0b3JzIGNvZGlmeSB5b3VyIGRhdGEgaW50byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMNCg0KQWgsIHRoZSBkcmVhZGVkIGZhY3RvcnMhIEEgZmFjdG9yIGlzIGEgKmNsYXNzKiBvZiBvYmplY3QgdXNlZCB0byBlbmNvZGUgYSBjaGFyYWN0ZXIgdmVjdG9yIGludG8gY2F0ZWdvcmllcy4gVGhleSBhcmUgdXNlZCB0byBzdG9yZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYW5kIGFsdGhvdWdoIGl0IGlzIHRlbXB0aW5nIHRvIHRoaW5rIG9mIHRoZW0gYXMgY2hhcmFjdGVyIHZlY3RvcnMgdGhpcyBpcyBhICoqZGFuZ2Vyb3VzIG1pc3Rha2UqKi4gQWRkaW5nIG9yIGNoYW5naW5nIGRhdGEgaW4gYSBkYXRhIGZyYW1lIHdpdGggcHJlLWV4aXN0aW5nIGZhY3RvcnMgcmVxdWlyZXMgdGhhdCB5b3UgbWF0Y2ggZmFjdG9yIGxldmVscyBjb3JyZWN0bHkgYXMgd2VsbC4NCg0KRmFjdG9ycyBtYWtlIHBlcmZlY3Qgc2Vuc2UgaWYgeW91IGFyZSBhIHN0YXRpc3RpY2lhbiBkZXNpZ25pbmcgYSBwcm9ncmFtbWluZyBsYW5ndWFnZSAoISkgYnV0IHRvIGV2ZXJ5b25lIGVsc2UgdGhleSBleGlzdCBzb2xlbHkgdG8gdG9ybWVudCB1cyB3aXRoIGNvbmZ1c2luZyBlcnJvcnMuIEF0IGl0cyBjb3JlLCBhIGZhY3RvciBpcyByZWFsbHkganVzdCBhbiBpbnRlZ2VyIHZlY3RvciBvciBjaGFyYWN0ZXIgZGF0YSB3aXRoIGFuIGFkZGl0aW9uYWwgYXR0cmlidXRlLCBjYWxsZWQgYGxldmVscygpYCwgd2hpY2ggZGVmaW5lcyB0aGUgYWNjZXB0ZWQgdmFsdWVzIGZvciB0aGF0IHZhcmlhYmxlLg0KDQojIyMgMi4zLjAuMSBXaHkgdXNlIGZhY3RvcnM/DQoNCldoeSBub3QganVzdCB1c2UgY2hhcmFjdGVyIHZlY3RvcnMsIHlvdSBhc2s/DQoNCkJlbGlldmUgaXQgb3Igbm90IGZhY3RvcnMgZG8gaGF2ZSBzb21lIHVzZWZ1bCBwcm9wZXJ0aWVzLiBGb3IgZXhhbXBsZSwgZmFjdG9ycyBhbGxvdyB5b3UgdG8gc3BlY2lmeSBhbGwgcG9zc2libGUgdmFsdWVzIGEgdmFyaWFibGUgbWF5IHRha2UgZXZlbiBpZiB0aG9zZSB2YWx1ZXMgYXJlIG5vdCBpbiB5b3VyIGRhdGEgc2V0LiBUaGluayBvZiAqY29uZGl0aW9uYWwgZm9ybWF0dGluZyogaW4gRXhjZWwuIFdlIGFsc28gdXNlIHRoZW0gaGVhdmlseSBpbiBnZW5lcmF0aW5nIHN0YXRpc3RpY2FsIGFuYWx5c2VzIGFuZCBpbiBncm91cGluZyBkYXRhIHdoZW4gd2Ugd2FudCB0byB2aXN1YWxpemUgaXQuDQoNCiMjIyAyLjMuMC4yIEEgaGlzdG9yaWNhbCBub3RlIGFib3V0IFIgNC4wLnggdmVyc3VzIHIgMy54LngNCg0KU2luY2UgdGhlIGluY2VwdGlvbiBvZiBSLCBgZGF0YS5mcmFtZSgpYCBjYWxscyBoYXZlIGJlZW4gdXNlZCB0byBjcmVhdGUgZGF0YSBmcmFtZXMgKipidXQqKiB0aGUgZGVmYXVsdCBiZWhhdmlvdXIgd2FzIHRvIGNvbnZlcnQgc3RyaW5ncyAoYW5kIGNoYXJhY3RlcnMpIHRvIGZhY3RvcnMhIFRoaXMgaXMgYSB0aHJvd2JhY2sgdG8gdGhlIHB1cnBvc2Ugb2YgUiwgd2hpY2ggd2FzIHRvIHBlcmZvcm0gc3RhdGlzdGljYWwgYW5hbHlzZXMgb24gZGF0YXNldHMgd2l0aCBtZXRob2RzIGxpa2UgKipBTk9WQSoqIHdoaWNoIGV4YW1pbmUgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMgKGllIGZhY3RvcnMpIQ0KDQpBcyBSIGhhcyBiZWNvbWUgbW9yZSBwb3B1bGFyIGFuZCBpdHMgYXBwbGljYXRpb25zIGFuZCBwYWNrYWdlcyBoYXZlIGV4cGFuZGVkLCBpbmNvbWluZyB1c2VycyBoYXZlIGJlZW4gZmFjZWQgd2l0aCByZW1lbWJlcmluZyB0aGlzIG9ic2N1cmUgYmVoYXZpb3VyLCBsZWFkaW5nIHRvIGxvc3QgaG91cnMgb2YgZGVidWdnaW5nIGdyaWVmIGFzIHRoZXkgd29uZGVyIHdoeSB0aGV5IGNhbid0IHB1bGwgaW5mb3JtYXRpb24gZnJvbSB0aGVpciBkYXRhZnJhbWVzIHRvIGRvIGEgc2ltcGxlIGFuYWx5c2lzIG9uICpDLiBlbGVnYW5zKiBzdHJhaW4gYWJ1bmRhbmNlIHZpYSBtb2xlY3VsYXIgaW52ZXJzaW9uIHByb2JlcyBpbiBkYXRhc2V0cyBvZiBtdWx0aXBsZXhlZCBwb3B1bGF0aW9ucy4gKiojU3VzcGNpb3VzbHlTcGVjaWZpYyoqDQoNClRoYXQgbWVhbnQgdGhhdCB1c2VycyB1c3VhbGx5IGhhZCB0byBjcmVhdGUgZGF0YSBmcmFtZXMgaW5jbHVkaW5nIHRoZSB0b2dnbGUNCg0KYGRhdGEuZnJhbWUobmFtZT1jaGFyYWN0ZXIoKSwgdmFsdWU9bnVtZXJpYygpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpYA0KDQpGcmV0IG5vIG1vcmUhIEFzIG9mICoqUiA0LngueCoqIHRoZSBkZWZhdWx0IGJlaGF2aW91ciBoYXMgc3dpdGNoZWQgYW5kIGBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0VgIGlzIHRoZSAqKmRlZmF1bHQqKiEgTm93IGlmIHdlIHdhbnQgb3VyIGNoYXJhY3RlcnMgdG8gYmUgZmFjdG9ycywgd2UgbXVzdCBjb252ZXJ0IHRoZW0gKioqZXhwbGljaXRseSoqKiwgb3IgdHVybiB0aGlzIGJlaGF2aW91ciBvbiBhdCB0aGUgb3V0c2V0IG9mIGNyZWF0aW5nIGVhY2ggZGF0YSBmcmFtZSENCg0KYGBge3J9DQojIEdlbmVyYXRlIGEgZGF0YSBmcmFtZSBhbmQgaW5jbHVkZSBmYWN0b3JzDQpzdHIoZGF0YS5mcmFtZShjb3VudHJ5ID0gY2hhcmFjdGVyLnZlY3RvciwNCiAgICAgICAgICAgICAgIHZhbHVlcyA9IG51bWVyaWMudmVjdG9yWzI6NF0sDQogICAgICAgICAgICAgICBjb21tb253ZWFsdGggPSBsb2dpY2FsLnZlY3RvclsxOjNdLA0KICAgICAgICAgICAgICAgY29udGluZW50ID0gYygiTm9ydGggQW1lcmljYSIsICJOb3J0aCBBbWVyaWNhIiwgIkV1cm9wZSIpLA0KICAgICAgICAgICAgICAgLi4uKQ0KICAgICkNCmBgYA0KDQpgYGB7cn0NCiMgRXhwbGljaXRseSBkZWZpbmUgZmFjdG9ycyBmb3IgZWFjaCB2YXJpYWJsZS4NCnN0cihkYXRhLmZyYW1lKGNvdW50cnkgPSAuLi4oY2hhcmFjdGVyLnZlY3RvciksDQogICAgICAgICAgICAgICB2YWx1ZXMgPSBudW1lcmljLnZlY3RvclsyOjRdLA0KICAgICAgICAgICAgICAgY29tbW9ud2VhbHRoID0gbG9naWNhbC52ZWN0b3JbMTozXSwNCiAgICAgICAgICAgICAgIGNvbnRpbmVudCA9IGMoIk5vcnRoIEFtZXJpY2EiLCAiTm9ydGggQW1lcmljYSIsICJFdXJvcGUiKSwNCiAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCiAgICApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjMuMSBTcGVjaWZ5IGBmYWN0b3JzYCBhbmQgdGhlaXIgbGV2ZWxzIGV4cGxpY2l0bHkgZHVyaW5nIG9yIGFmdGVyIGBkYXRhLmZyYW1lYCBjcmVhdGlvbg0KDQpGcm9tIGFib3ZlLCB5b3UgY2FuIHNwZWNpZnkgd2hpY2ggY29sdW1ucyBvZiBzdHJpbmdzIGFyZSBjb252ZXJ0ZWQgdG8gZmFjdG9ycyBhdCB0aGUgdGltZSBvZiBkZWNsYXJpbmcgeW91ciBjb2x1bW4gaW5mb3JtYXRpb24uIEFsdGVybmF0aXZlbHkgeW91IGNhbiBjb2VyY2UgY2hhcmFjdGVyIHZlY3RvcnMgdG8gZmFjdG9ycyBhZnRlciBnZW5lcmF0aW5nIHRoZW0uDQoNClIncyBkZWZhdWx0IGJlaGF2aW91ciBwdXRzIGZhY3RvciBsZXZlbHMgaW4gKmFscGhhYmV0aWNhbCBvcmRlciouIFRoaXMgY2FuIGNhdXNlIHByb2JsZW1zIGlmIHdlIGFyZW4ndCBhd2FyZSBvZiBpdC4gWW91IGNhbiBjaGVjayB0aGUgb3JkZXIgb2YgeW91ciBmYWN0b3IgbGV2ZWxzIHdpdGggdGhlIGBsZXZlbHMoKWAgY29tbWFuZC4gRnVydGhlcm1vcmUgeW91IGNhbiBzcGVjaWZ5LCBkdXJpbmcgZmFjdG9yIGNyZWF0aW9uLCB5b3VyIGxldmVsIG9yZGVyLg0KDQoqKkFsd2F5cyBjaGVjayB0byBtYWtlIHN1cmUgeW91ciBmYWN0b3IgbGV2ZWxzIGFyZSB3aGF0IHlvdSBleHBlY3QuKioNCg0KV2l0aCBmYWN0b3JzLCB3ZSBjYW4gZGVhbCB3aXRoIG91ciBjaGFyYWN0ZXIgbGV2ZWxzIGRpcmVjdGx5LCBvciB0aGVpciBudW1lcmljIGVxdWl2YWxlbnRzLg0KDQpgYGB7cn0NCiMgR2VuZXJhdGUgYSBkYXRhIGZyYW1lIGFuZCBpbmNsdWRlIGZhY3RvcnMNCnN0cihkYXRhLmZyYW1lKGNvdW50cnkgPSBjaGFyYWN0ZXIudmVjdG9yLA0KICAgICAgICAgICAgICAgdmFsdWVzID0gbnVtZXJpYy52ZWN0b3JbMjo0XSwNCiAgICAgICAgICAgICAgIGNvbW1vbndlYWx0aCA9IGxvZ2ljYWwudmVjdG9yWzE6M10sDQogICAgICAgICAgICAgICBjb250aW5lbnQgPSBmYWN0b3IoYygiTm9ydGggQW1lcmljYSIsICJOb3J0aCBBbWVyaWNhIiwgIkV1cm9wZSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4uLiA9IGMoIk5vcnRoIEFtZXJpY2EiLCAiRXVyb3BlIikpDQogICAgICAgICAgICAgICkNCiAgICkNCmBgYA0KDQpgYGB7cn0NCiMgQ29lcmNlIGEgZmFjdG9yDQptaXhlZC5kZiA8LSBkYXRhLmZyYW1lKGNvdW50cnkgPSBjaGFyYWN0ZXIudmVjdG9yLA0KICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IG51bWVyaWMudmVjdG9yWzI6NF0sDQogICAgICAgICAgICAgICAgICAgICAgY29tbW9ud2VhbHRoID0gbG9naWNhbC52ZWN0b3JbMTozXSwNCiAgICAgICAgICAgICAgICAgICAgICBjb250aW5lbnQgPSBjKCJOb3J0aCBBbWVyaWNhIiwgIk5vcnRoIEFtZXJpY2EiLCAiRXVyb3BlIikpDQoNCiMgU2V0IG91ciBmYWN0b3IgYWZ0ZXIgZGVjbGFyaW5nIHRoZSBkYXRhIGZyYW1lDQptaXhlZC5kZiRjb250aW5lbnQgPC0gZmFjdG9yKC4uLiwgbGV2ZWxzPWMoIk5vcnRoIEFtZXJpY2EiLCAiRXVyb3BlIikpDQoNCg0Kc3RyKG1peGVkLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi4zLjIgTW9yZSBmYWN0cyBhYm91dCBmYWN0b3JzDQoNCjEuICBVc2UgYGxldmVscygpYCB0byBsaXN0IHRoZSBsZXZlbHMgYW5kIHRoZWlyIG9yZGVyIGZvciB5b3VyIGZhY3Rvcg0KDQoyLiAgVG8gcmVuYW1lIGxldmVscyBvZiBhIGZhY3RvciwgZGVjbGFyZSBhbmQgcmVhc3NpZ24geW91ciBmYWN0b3IuDQoNCjMuICBNb3ZlIGEgc2luZ2xlIGxldmVsIHRvIHRoZSBmaXJzdCBwb3NpdGlvbiB3aXRoaW4geW91ciBmYWN0b3IgbGV2ZWxzIHdpdGggYHJlbGV2ZWwoKWAuDQoNCjQuICBGYWN0b3IgbGV2ZWxzIGNhbiBiZSBhc3NpZ25lZCBhbiBvcmRlciBvZiBwcmVjZWRlbmNlIGR1cmluZyB0aGVpciBjcmVhdGlvbiB3aXRoIHRoZSBwYXJhbWV0ZXIgYG9yZGVyZWQgPSBUUlVFYC4NCg0KNS4gIERlZmluZSBsYWJlbHMgZm9yIHlvdXIgZmFjdG9yIGR1cmluZyB0aGVpciBjcmVhdGlvbnMgd2l0aCB0aGUgcGFyYW1ldGVyIGBsYWJlbHMgPSBjKClgLiBOb3RlIHRoYXQgbGV2ZWwgb3JkZXIgaXMgYXNzaWduZWQgKmJlZm9yZSogbGFiZWxzIGFyZSBhZGRlZCB0byB5b3VyIGRhdGEuIFlvdSBhcmUgZXNzZW50aWFsbHkgbGFiZWxpbmcgdGhlICppbnRlZ2VyKiBhc3NpZ25lZCB0byB5b3VyIGZhY3RvciBsZXZlbHMgc28gYmUgY2FyZWZ1bCB3aGVuIHVzaW5nIHRoaXMgcGFyYW1ldGVyIQ0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXN1Y2Nlc3N9DQo8Yj5BZHZhbmNlZCBmYWN0b3JzIGZ1bmN0aW9ucyB3aXRoIGZvcmNhdHM8L2I+IElmIHlvdSdyZSBsb29raW5nIGZvciBtb3JlIGFkdmFuY2VkIGZ1bmN0aW9ucyB0aGF0IHlvdSBjYW4gdXNlIHRvIG1hbmlwdWxhdGUsIHNvcnQgb3IgdXBkYXRlIGZhY3RvcnMsIGNoZWNrIG91dCB0aGUgPGEgaHJlZj0iaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2ZjdF9yZWxldmVsLmh0bWwiPjxiPmZvcmNhdHMgZnVuY3Rpb248L2I+PC9hPi4gV2l0aCBpdCwgeW91IGNhbiByZWZhY3RvciBiYXNlZCBvbiBmdW5jdGlvbnMsIGZyZXF1ZW5jeSwgb3IgZXhwbGljaXRseSByZS1zcGVjaWZ5IHRoZSBvcmRlciBvZiBvbmUgb3IgbW9yZSBmYWN0b3IgbGV2ZWxzLiBXZSdsbCBzZWUgdGhpcyBwYWNrYWdlIGluIGFjdGlvbiBpbiBtb3JlIGRldGFpbCBkdXJpbmcgbGF0ZXIgbGVjdHVyZXMuDQo6OjoNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9tYXRyaXgtYWRkaXRpb24uZ2lmP3Jhdz10cnVlIiB3aWR0aD0iNTAwIi8+DQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDIuNC4wIE1hdGhlbWF0aWNhbCBvcGVyYXRpb25zIG9uIGRhdGEgZnJhbWVzIGFuZCBhcnJheXMNCg0KWWVzLCB5b3UgY2FuIHRyZWF0IGRhdGEgZnJhbWVzIGFuZCBhcnJheXMgbGlrZSBsYXJnZSBsaXN0cyB3aGVyZSBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucyBjYW4gYmUgYXBwbGllZCB0byBpbmRpdmlkdWFsIGVsZW1lbnRzIG9yIHRvIGVudGlyZSBjb2x1bW5zIG9yIG1vcmUhDQoNCiMjIyAyLjQuMSBNYXRoZW1hdGljYWwgb3BlcmF0aW9ucyBhcmUgYXBwbGllZCBkaWZmZXJlbnRseSBkZXBlbmRpbmcgb24gZGF0YSB0eXBlDQoNCi0gICAqKm51bWVyaWMgZGF0YSoqOiBvcGVyYXRpb25zIGFwcGxpZWQgYXMgZXhwZWN0ZWQNCi0gICAqKm5vbi1udW1lcmljKiogKGllIGNoYXJhY3RlcnMpOiBlcnJvciB3aWxsIGJlIHRocm93bg0KLSAgICoqZmFjdG9ycyoqOiB3YXJuaW5nIG1lc3NhZ2UgYW5kIE5BcyByZXR1cm5lZA0KLSAgICoqbG9naWNhbCBkYXRhKiogKGBUUlVFYC9gRkFMU0VgKTogY29lcmNpb24gdG8gbnVtZXJpYyBiZWZvcmUgYXBwbHlpbmcgb3BlcmF0aW9ucw0KDQpUaGVyZWZvcmUgYmUgY2FyZWZ1bCB0byBzcGVjaWZ5IHlvdXIgbnVtZXJpYyBkYXRhIGZvciBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucy4NCg0KYGBge3IsIGVycm9yPVRSVUV9DQptaXhlZC5kZg0KDQojIEFkZCB0byBlYWNoIGVsZW1lbnQNCm1peGVkLmRmJHZhbHVlcyArIDMNCg0KIyBBZGQgY29sdW1ucyB0byBlYWNoIG90aGVyDQptaXhlZC5kZiR2YWx1ZXMgKyBtaXhlZC5kZiR2YWx1ZXMNCg0KIyBtdWx0aXBseSBlYWNoIGVsZW1lbnQgYnkgYSBjb25zdGFudA0KbWl4ZWQuZGYkdmFsdWVzICogNA0KDQojIGltcGxpY2l0IGNvZXJjaW9uIG9mIGxvZ2ljYWwgdG8gaW50ZWdlcg0KbWl4ZWQuZGYkY29tbW9ud2VhbHRoICogNSANCg0KIyBQZXJmb3JtIG1hdGggb24gYSBmYWN0b3INCm1peGVkLmRmJGNvbnRpbmVudCAqIDYNCg0KIyBDb252ZXJ0IHRoZSBmYWN0b3IgdG8gYSBudW1lcmljIGZpcnN0DQphcy5udW1lcmljKG1peGVkLmRmJGNvbnRpbmVudCkgKiA3DQoNCiMgQ2FuIHdlIHBlcmZvcm0gbWF0aCBvbiBub24tbnVtZXJpYyB2YXJpYWJsZXM/DQouLi4NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi41LjAgVXNpbmcgdGhlIGBhcHBseSgpYCBmYW1pbHkgb2YgZnVuY3Rpb25zIHRvIHBlcmZvcm0gYWN0aW9ucyBhY3Jvc3MgZGF0YSBzdHJ1Y3R1cmVzDQoNClRoZSBhYm92ZSBhcmUgaWxsdXN0cmF0aXZlIGV4YW1wbGVzIHRvIHNlZSBob3cgb3VyIGRpZmZlcmVudCBkYXRhIHN0cnVjdHVyZXMgYmVoYXZlLiBJbiByZWFsaXR5LCB5b3Ugd2lsbCB3YW50IHRvIGRvIGNhbGN1bGF0aW9ucyBhY3Jvc3Mgcm93cyBhbmQgY29sdW1ucywgYW5kIG5vdCBvbiB5b3VyIGVudGlyZSBtYXRyaXggb3IgZGF0YSBmcmFtZS4NCg0KIyMjIDIuNS4xIFRoZSBgYXBwbHkoKWAgZnVuY3Rpb24gd2lsbCByZWNvZ25pemUgYmFzaWMgZnVuY3Rpb25zIGFuZCB1c2UgdGhlbSBvbiB2ZWN0b3JpemVkIGRhdGENCg0KVGhlIGFib3ZlIGFyZSBpbGx1c3RyYXRpdmUgZXhhbXBsZXMgdG8gc2VlIGhvdyBvdXIgZGlmZmVyZW50IGRhdGEgc3RydWN0dXJlcyBiZWhhdmUuIEluIHJlYWxpdHksIHlvdSB3aWxsIHdhbnQgdG8gZG8gY2FsY3VsYXRpb25zIGFjcm9zcyByb3dzIGFuZCBjb2x1bW5zLCBhbmQgbm90IG9uIHlvdXIgZW50aXJlIG1hdHJpeCBvciBkYXRhIGZyYW1lLg0KDQpGb3IgZXhhbXBsZSwgd2UgbWlnaHQgaGF2ZSBhIGNvdW50IHRhYmxlIHdoZXJlIHJvd3MgYXJlIGdlbmVzLCBjb2x1bW5zIGFyZSBzYW1wbGVzLCBhbmQgd2Ugd2FudCB0byBrbm93IHRoZSBzdW0gb2YgYWxsIHRoZSBjb3VudHMgZm9yIGEgZ2VuZS4gVG8gZG8gdGhpcywgd2UgY2FuIHVzZSB0aGUgYGFwcGx5KClgIGZ1bmN0aW9uLiBgYXBwbHkoKWAgVGFrZXMgYW4gYXJyYXksIG1hdHJpeCAob3Igc29tZXRoaW5nIHRoYXQgY2FuIGJlIGNvZXJjZWQgYXMgc3VjaCwgbGlrZSBhIG51bWVyaWMgZGF0YSBmcmFtZSksIGFuZCBhcHBsaWVzIGEgZnVuY3Rpb24gb3ZlciByb3dzIG9yIGNvbHVtbnMuIFRoZSBgYXBwbHkoKWAgZnVuY3Rpb24gdGFrZXMgdGhlIGZvbGxvd2luZyBwYXJhbWV0ZXJzOg0KDQotICAgYFhgOiBhbiBhcnJheS4gbWF0cml4IG9yIHNvbWV0aGluZyB0aGF0IGNhbiBiZSBjb2VyY2VkIHRvIHRoZXNlIG9iamVjdHMNCi0gICBgTUFSR0lOYDogZGVmaW5lcyBob3cgdG8gYXBwbHkgdGhlIGZ1bmN0aW9uOyBgMWAgPSByb3dzLCBgMmAgPSBjb2x1bW5zLg0KLSAgIGBGVU5gOiB0aGUgZnVuY3Rpb24gdG8gYmUgYXBwbGllZC4gU3VwcGxpZWQgYXMgYSBmdW5jdGlvbiBuYW1lICoqKndpdGhvdXQqKiogdGhlIGAoKWAgc3VmZml4DQotICAgYC4uLmA6IHRoaXMgbm90YXRpb24gbWVhbnMgd2UgY2FuIHBhc3MgYWRkaXRpb25hbCBwYXJhbWV0ZXJzIHRvIG91ciBmdW5jdGlvbiBkZWZpbmVkIGJ5IGBGVU5gLg0KDQphbmQgcmV0dXJucyBhIHZlY3RvciwgYXJyYXkgb3IgbGlzdCBkZXBlbmRpbmcgb24gdGhlIG5hdHVyZSBvZiBYLg0KDQpMZXQncyBwcmFjdGljZSBieSBpbnZva2luZyB0aGUgYHN1bWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KIyBNYWtlIGEgc2FtcGxlIGRhdGEgZnJhbWUgb2YgbnVtZXJpYyB2YWx1ZXMgb25seQ0KbnVtZXJpYy5kZiA9IGRhdGEuZnJhbWUoZ2VuZUEgPSBudW1lcmljLnZlY3RvciwgZ2VuZUIgPSBudW1lcmljLnZlY3RvcioyLCBnZW5lQyA9IG51bWVyaWMudmVjdG9yKjMpDQpudW1lcmljLmRmDQoNCiMgQXBwbHkgc3VtIGJ5IHJvd3MNCmFwcGx5KG51bWVyaWMuZGYsIC4uLiwgc3VtKQ0KDQojIEFwcGx5IHN1bSBieSBjb2x1bW5zDQphcHBseShudW1lcmljLmRmLCAuLi4sIHN1bSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDIuNS4yIFRoZSBvdGhlciBtZW1iZXJzIG9mIHRoZSBgYXBwbHkoKWAgZmFtaWx5DQoNClRoZXJlIGFyZSAzIGFkZGl0aW9uYWwgbWVtYmVycyBvZiB0aGUgYGFwcGx5KClgIGZhbWlseSB0aGF0IHBlcmZvcm0gc2ltaWxhciBmdW5jdGlvbnMgd2l0aCB2YXJ5aW5nIG91dHB1dHMNCg0KMS4gIGBsYXBwbHkoZGF0YSwgRlVOLCAuLi4pYCBpcyB1c2FibGUgb24gZGF0YWZyYW1lcywgbGlzdHMsIGFuZCB2ZWN0b3JzLiBJdCByZXR1cm5zIGEgYGxpc3RgIGFzIG91dHB1dC4NCg0KLSAgIEl0IHdpbGwgY29lcmNlIG5vbi1saXN0IG9iamVjdHMgdG8gYSBsaXN0DQotICAgQWRkaXRpb25hbCBhcmd1bWVudHMgdG8gYEZVTmAgd2lsbCBiZSBhcHBsaWVkIGZyb20gdGhlIGAuLi5gDQoNCjIuICBgc2FwcGx5KGRhdGEsIEZVTiwgLi4uKWAgd29ya3Mgc2ltaWxhcmx5IHRvIGBsYXBwbHkoKWAgZXhjZXB0IGl0IHRyaWVzIHRvIHNpbXBsaWZ5IHRoZSBvdXRwdXQgdG8gdGhlIG1vc3QgZWxlbWVudGFyeSBkYXRhIHN0cnVjdHVyZSBwb3NzaWJsZS4gaS5lLiBpdCB3aWxsIHJldHVybiB0aGUgc2ltcGxlc3QgZm9ybSBvZiB0aGUgZGF0YSB0aGF0IG1ha2VzIHNlbnNlIGFzIGEgcmVwcmVzZW50YXRpb24uDQoNCjMuICBgbWFwcGx5KEZVTiwgZGF0YSwgLi4uKWAgaXMgc2hvcnQgZm9yICJtdWx0aXZhcmlhdGUiIGFwcGx5IGFuZCBpdCBhcHBsaWVzIGEgZnVuY3Rpb24gdG8gbXVsdGlwbGUgbGlzdHMgb3IgbXVsdGlwbGUgdmVjdG9yIGFyZ3VtZW50cy4NCg0KYGBge3J9DQojIFVzZSBsYXBwbHkgb24gdGhlIGNvbHVtbnMgb2YgbnVtZXJpYy5kZg0KLi4uKG51bWVyaWMuZGYsIHN1bSkNCnN0cihsYXBwbHkobnVtZXJpYy5kZiwgc3VtKSkNCmBgYA0KDQpgYGB7cn0NCiMgVXNlIHNhcHBseSBvbiB0aGUgY29sdW1ucyBvZiBudW1lcmljLmRmDQouLi4obnVtZXJpYy5kZiwgc3VtKQ0Kc3RyKHNhcHBseShudW1lcmljLmRmLCBzdW0pKQ0KYGBgDQoNCmBgYHtyfQ0KIyBVc2luZyBsYXBwbHkgYW5kIHNhcHBseSBhbmQgc3VtIG9uIGFuIGFjdHVhbCBsaXN0DQpzdW0ubGlzdCA8LSBsaXN0KC4uLikNCnN0cihzdW0ubGlzdCkNCg0KIyBsYXBwbHkgb24gdGhlIGxpc3QgcmV0dXJucyBhIGxpc3QNCmxhcHBseShzdW0ubGlzdCwgc3VtKQ0KDQojIHNhcHBseSBvbiB0aGUgbGlzdCByZXR1cm5zIGEgdmVjdG9yDQpzYXBwbHkoc3VtLmxpc3QsIHN1bSkNCmBgYA0KDQpgYGB7cn0NCiMgVXNlIGxhcHBseSB0byBzZWxlY3QgcG9ydGlvbnMgZnJvbSBhIGxpc3QNCnN1bS5saXN0IDwtIGxpc3QobnVtZXJpYy5kZiwgbnVtZXJpYy5kZikNCg0KIyBFeHRyYWN0IHRoZSBmaXJzdCByb3cgZnJvbSBlYWNoIG1lbWJlciBvZiB0aGUgbGlzdA0KbGFwcGx5KHN1bS5saXN0LCAuLi4pDQoNCiMgRXh0cmFjdCB0aGUgMm5kIGNvbHVtbiBmcm9tIGVhY2ggbWVtYmVyIG9mIHRoZSBsaXN0DQpsYXBwbHkoc3VtLmxpc3QsICJbIiwgLCAyKQ0KDQojIFRha2UgYSBjbG9zZSBsb29rIGF0IHdoYXQgc2FwcGx5IHJldHVybnMgaW4gdGhpcyBjYXNlDQpzYXBwbHkoc3VtLmxpc3QsICJbIiwgLCAyKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpOb3RpY2UgaG93IGluIHVzaW5nIGBzYXBwbHkoKWAgdG8gZXh0cmFjdCBmcm9tIGEgbGlzdCBvZiBkYXRhIGZyYW1lcywgYSBzaW5nbGUgbWF0cml4IHdhcyByZXR1cm5lZCAtIGEgc2luZ2xlIG91dHB1dCBpbiB0aGUgc2ltcGxlc3QgZm9ybSB0aGF0IG1haW50YWlucyBzdHJ1Y3R1cmUuDQoNCk5vdyBsZXQncyBnaXZlIGBtYXBwbHkoKWAgYSB0cnkuDQoNCmBgYHtyfQ0KIyBVc2UgbWFwcGx5IGluIGFuIGV4YW1wbGUgb24gbnVtZXJpYy52ZWN0b3INCm1hcHBseShzdW0sIG51bWVyaWMudmVjdG9yLCBudW1lcmljLnZlY3RvcikNCg0KIyBVc2UgbWFwcGx5IGluIGFuIGV4YW1wbGUgb24gbnVtZXJpYy5kZg0KbWFwcGx5KHN1bSwgbnVtZXJpYy5kZiwgbnVtZXJpYy5kZikNCg0KIyBVc2UgbWFwcGx5IG9uIHRoZSByZXAgZnVuY3Rpb24gdG8gc2VlIGl0cyBvdXRwdXQNCm1hcHBseShyZXAsIGMoLi4uKSwgNCkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L25vdF9hdmFpbGFibGUucG5nP3Jhdz10cnVlIiB3aWR0aD0iNzAwIi8+DQo6OjoNCg0KIyMgMi42LjAgU3BlY2lhbCBkYXRhOiBgTkFgIGFuZCBgTmFOYCB2YWx1ZXMNCg0KTWlzc2luZyB2YWx1ZXMgaW4gUiBhcmUgaGFuZGxlZCBhcyBgTkFgIChOb3QgQXZhaWxhYmxlKS4gSW1wb3NzaWJsZSB2YWx1ZXMgKGxpa2UgdGhlIHJlc3VsdHMgb2YgZGl2aWRpbmcgYnkgemVybykgYXJlIHJlcHJlc2VudGVkIGJ5IGBOYU5gIChOb3QgYSBOdW1iZXIpLiBUaGVzZSB0eXBlcyBvZiB2YWx1ZXMgY2FuIGJlIGNvbnNpZGVyZWQgKm51bGwgdmFsdWVzKi4gVGhlc2UgdHdvIHR5cGVzIG9mIHZhbHVlcywgc3BlY2lhbGx5IE5BcywgaGF2ZSBzcGVjaWFsIHdheXMgdG8gYmUgZGVhbHQgd2l0aCwgb3RoZXJ3aXNlIGl0IG1heSBsZWFkIHRvIGVycm9ycyBpbiBzb21lIGZ1bmN0aW9ucy4NCg0KRm9yIG91ciBwdXJwb3Nlcywgd2UgYXJlIG5vdCBpbnRlcmVzdGVkIGluIGtlZXBpbmcgYE5BYCBkYXRhIHdpdGhpbiBvdXIgZGF0YXNldHMgc28gd2Ugd2lsbCB1c3VhbGx5IGRldGVjdCBhbmQgcmVtb3ZlIHRoZW0gb3IgcmVwbGFjZSB0aGVtIHdpdGhpbiBvdXIgZGF0YSBhZnRlciBpdCBpcyBpbXBvcnRlZC4NCg0KIyMjIDIuNi4xIEhlbHBmdWwgZnVuY3Rpb25zIGFuZCBpbmZvcm1hdGlvbiBmb3IgZGVhbGluZyB3aXRoIGBOQWAgZGF0YQ0KDQoxLiAgYGlzLm5hKClgIHJldHVybnMgYSBsb2dpY2FsIHZlY3RvciByZXBvcnRpbmcgd2hpY2ggdmFsdWVzIGZyb20geW91ciBxdWVyeSBhcmUgYE5BYC4NCjIuICBgY29tcGxldGUuY2FzZXMoKWAgcmV0dXJucyBhIGxvZ2ljYWwgZm9yIHJvdyB3aXRob3V0IGFueSBgTkFgIHZhbHVlcy4NCjMuICBTb21lIGZ1bmN0aW9ucyBjYW4gaWdub3JlIGBOQWAgdmFsdWVzIHdpdGggdGhlIGBuYS5ybSA9IFRSVUVgIHBhcmFtZXRlcjogaWUgYG1lYW4oKWAsIGBzdW0oKWAgZXRjLg0KNC4gIEFkZGl0aW9uYWwgZnVuY3Rpb25zIGluIHRoZSBgdGlkeXJgIHBhY2thZ2UgY2FuIGFsc28gYmUgdXNlZCB0byB3b3JrIHdpdGggYE5BYCB2YWx1ZXMuDQoNCmBgYHtyfQ0KIyBBZGQgc29tZSBOQXMgdG8gb3VyIGRhdGEgZnJhbWUNCm1peGVkLmRmIDwtIGRhdGEuZnJhbWUoY291bnRyeSA9IGNoYXJhY3Rlci52ZWN0b3IsDQogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAuLi4sIDkpLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1vbndlYWx0aCA9IGxvZ2ljYWwudmVjdG9yWzE6M10sDQogICAgICAgICAgICAgICAgICAgICAgY29udGluZW50ID0gYygiTm9ydGggQW1lcmljYSIsICJOb3J0aCBBbWVyaWNhIiwgIkV1cm9wZSIpLA0KICAgICAgICAgICAgICAgICAgICAgIG1lYXN1cmUgPSBjKCJtZXRyaWMiLCBOQSwgIm1ldHJpYyIpDQogICAgICAgICAgICAgICAgICAgICAgKQ0KIyBMb29rIGF0IG91ciB1cGRhdGVkIGRhdGEgZnJhbWUNCm1peGVkLmRmDQoNCiMgV2hpY2ggZW50cmllcyBhcmUgTkE/DQppcy5uYShtaXhlZC5kZikNCg0KIyBXaGljaCByb3dzIGFyZSBpbmNvbXBsZXRlPw0KY29tcGxldGUuY2FzZXMobWl4ZWQuZGYpDQoNCiMgVXNlIHNvbWUgbWF0aCBmdW5jdGlvbnMgDQpzdW0obWl4ZWQuZGYkdmFsdWVzLCAuLi4pDQpgYGANCg0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IDo6OiB7YWxpZ249ImNlbnRlciJ9IDxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei90aWR5ZGF0YV8yLmpwZz9yYXc9dHJ1ZSIgd2lkdGg9IjgwMCIvPiBFYWNoIGRhdGFzZXQgaGFzIGl0J3Mgb3duIHByb2JsZW1zLiBJbWFnZSBmcm9tOiA8aHR0cHM6Ly9jZnNzLnVjaGljYWdvLmVkdS9ub3Rlcy90aWR5LWRhdGEvPiA6OjogfA0KDQojIDMuMC4wIFdlbGNvbWUgdG8gdGhlIGB0aWR5dmVyc2VgDQoNCkxldCdzIGJlZ2luIHdpdGggc29tZSBkZWZpbml0aW9uczogLSAqKlZhcmlhYmxlKio6IEEgcGFydCBvZiBhbiBleHBlcmltZW50IHRoYXQgY2FuIGJlIGNvbnRyb2xsZWQsIGNoYW5nZWQsIG9yIG1lYXN1cmVkLiAtICoqT2JzZXJ2YXRpb24qKjogVGhlIHJlc3VsdHMgb2YgbWVhc3VyaW5nIHRoZSB2YXJpYWJsZXMgb2YgaW50ZXJlc3QgaW4gYW4gZXhwZXJpbWVudC4NCg0KLSAgICoqV2lkZS1mb3JtYXQgZGF0YSoqOiB2YXJpYWJsZXMgbWF5IGJlIGxpc3RlZCBpbiB0aGUgZmlyc3QgY29sdW1uLCBlYWNoIGZvcm1pbmcgYSByb3cgb2YgaXRzIG93bi4gT2JzZXJ2YXRpb25zIG1heSBiZSBwcmVzZW50ZWQgYXMgY29sdW1ucyB0aGF0IGZpbGwgb2JzZXJ2ZWQgdmFsdWVzIGZvciBlYWNoIHZhcmlhYmxlLg0KLSAgICoqTG9uZy1mb3JtYXQgZGF0YSoqOiBlYWNoIHZhcmlhYmxlIGlzIGl0cyBvd24gY29sdW1uLCBhbmQgdGhlIHJlc3VsdHMgb2YgZWFjaCBtZWFzdXJlZCB2YXJpYWJsZSBhcmUgcmVjb3JkZWQgaW4gcm93cy4NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9sYXRyaW5lc193aWRlX3RvX2xvbmcucG5nP3Jhdz10cnVlIiB3aWR0aD0iODAwIi8+DQo6OjoNCg0KSW4gZGF0YSBzY2llbmNlLCBsb25nIGZvcm1hdCBpcyBwcmVmZXJyZWQgb3ZlciB3aWRlIGZvcm1hdCBiZWNhdXNlIGl0IGFsbG93cyBmb3IgYW4gZWFzaWVyIGFuZCBtb3JlIGVmZmljaWVudCBzdWJzZXQgYW5kIG1hbmlwdWxhdGlvbiBvZiB0aGUgZGF0YS4gVG8gcmVhZCBtb3JlIGFib3V0IHdpZGUgYW5kIGxvbmcgZm9ybWF0cywgdmlzaXQgW2hlcmVdKGh0dHBzOi8vZWFnZXJleWVzLm9yZy9iYXNpY3Mvc3ByZWFkc2hlZXQtdGhpbmtpbmctdnMtZGF0YWJhc2UtdGhpbmtpbmcpLg0KDQoqKldoeSB0aWR5IGRhdGE/KioNCg0KRGF0YSBjbGVhbmluZy93cmFuZ2xpbmcgKG9yIGRlYWxpbmcgd2l0aCAnbWVzc3knIGRhdGEpIGFjY291bnRzIGZvciBhIGh1Z2UgY2h1bmsgb2YgYSBkYXRhIHNjaWVudGlzdCdzIHRpbWUuIFVsdGltYXRlbHksIHdlIHdhbnQgdG8gZ2V0IG91ciBkYXRhIGludG8gYSAndGlkeScgZm9ybWF0IChsb25nIGZvcm1hdCkgd2hlcmUgaXQgaXMgZWFzeSB0byBtYW5pcHVsYXRlLCBtb2RlbCBhbmQgdmlzdWFsaXplLiBIYXZpbmcgYSBjb25zaXN0ZW50IGRhdGEgc3RydWN0dXJlIGFuZCB0b29scyB0aGF0IHdvcmsgd2l0aCB0aGF0IHN0YW5kYXJkaXplZCBkYXRhIHN0cnVjdHVyZSBjYW4gaGVscCB0aGlzIHByb2Nlc3MgYWxvbmcuDQoNCioqSW4gVGlkeSBkYXRhKio6DQoNCjEuICBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uLg0KMi4gIEVhY2ggb2JzZXJ2YXRpb24gZm9ybXMgYSByb3cuDQozLiAgRXZlcnkgY2VsbCBpcyBhIHNpbmdsZSB2YWx1ZS4NCg0KVGhpcyBzZWVtcyBwcmV0dHkgc3RyYWlnaHRmb3J3YXJkLCBhbmQgaXQgaXMuIEl0IGlzIHRoZSBkYXRhc2V0cyB5b3UgZ2V0IHRoYXQgd2lsbCBub3QgYmUgc3RyYWlnaHRmb3J3YXJkLiBIYXZpbmcgYSBtYXAgb2Ygd2hlcmUgdG8gdGFrZSB5b3VyIGRhdGEgaXMgaGVscGZ1bCB0byB1bnJhdmVsaW5nIGl0cyBzdHJ1Y3R1cmUgYW5kIGdldHRpbmcgaXQgaW50byBhIHVzYWJsZSBmb3JtYXQuDQoNCiMjIyAzLjAuMSBUaGUgNSBtb3N0IGNvbW1vbiBwcm9ibGVtcyB3aXRoIG1lc3N5IGRhdGFzZXRzIGFyZToNCg0KLSAgIGNvbW1vbiBoZWFkZXJzIGFyZSB2YWx1ZXMsIG5vdCB2YXJpYWJsZSBuYW1lcw0KLSAgIG11bHRpcGxlIHZhcmlhYmxlcyBhcmUgc3RvcmVkIGluIG9uZSBjb2x1bW4NCi0gICB2YXJpYWJsZXMgYXJlIHN0b3JlZCBpbiBib3RoIHJvd3MgYW5kIGNvbHVtbnMNCi0gICBhIHNpbmdsZSB2YXJpYWJsZSBzdG9yZWQgaW4gbXVsdGlwbGUgdGFibGVzDQotICAgbXVsdGlwbGUgdHlwZXMgb2Ygb2JzZXJ2YXRpb25hbCB1bml0cyBhcmUgc3RvcmVkIGluIHRoZSBzYW1lIHRhYmxlDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtaW5mb30NCjxiPk9ic2VydmF0aW9uYWwgdW5pdHM6PC9iPiBPZiB0aGUgdGhyZWUgcnVsZXMsIHRoZSBpZGVhIG9mIG9ic2VydmF0aW9uYWwgdW5pdHMgbWlnaHQgYmUgdGhlIGhhcmRlc3QgdG8gZ3Jhc3AuIEFzIGFuIGV4YW1wbGUsIHlvdSBtYXkgYmUgdHJhY2tpbmcgYSBwdXBweSBwb3B1bGF0aW9uIGFjcm9zcyA0IHZhcmlhYmxlczogYWdlLCBoZWlnaHQsIHdlaWdodCwgZnVyIGNvbG91ci4gRWFjaCBvYnNlcnZhdGlvbiB1bml0IGlzIGEgcHVwcHkuIEhvd2V2ZXIsIHlvdSBtaWdodCBiZSB0cmFja2luZyA8Yj48aT50aGUgc2FtZSBwdXBwaWVzPC9pPjwvYj4gYWNyb3NzIG11bHRpcGxlIG1lYXN1cmVtZW50cyAtIHNvIGEgdGltZSBmYWN0b3IgYXBwbGllcy4gSW4gdGhhdCBjYXNlLCB0aGUgb2JzZXJ2YXRpb24gdW5pdCBub3cgYmVjb21lcyBwdXBweS10aW1lLiBJbiB0aGF0IGNhc2UsIGVhY2ggcHVwcHktdGltZSBtZWFzdXJlbWVudCBiZWxvbmdzIGluIGEgZGlmZmVyZW50IHRhYmxlIChhdCBsZWFzdCBieSB0aWR5IGRhdGEgc3RhbmRhcmRzKS4gVGhpcywgaG93ZXZlciwgaXMgYSBzaW1wbGUgZXhhbXBsZSBhbmQgdGhpbmdzIGNhbiBnZXQgbW9yZSBjb21wbGV4IHdoZW4gdGFraW5nIGludG8gY29uc2lkZXJhdGlvbiB3aGF0IGRlZmluZXMgYW4gb2JzZXJ2YXRpb25hbCB1bml0LiBDaGVjayBvdXQgdGhpcyBibG9nIHBvc3QgYnkgPGEgaHJlZj0iaHR0cHM6Ly9jbGF1c3dpbGtlLmNvbS9ibG9nLzIwMTQvMDcvMjEva2VlcC15b3VyLWRhdGEtdGlkeS1wYXJ0LWlpLyI+Q2xhdXMgTy4gV2lsa2U8L2E+IGZvciBhIGxpdHRsZSBtb3JlIGV4cGxhbmF0aW9uLg0KOjo6DQoNCkxldCdzIGJlZ2luIHRoaXMgam91cm5leSB3aXRoIGRhdGEgaW1wb3J0Lg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4xLjAgT3BlbmluZyBhbmQgc2F2aW5nIGZpbGVzIHdpdGggdGhlIGByZWFkcmAgcGFja2FnZSAtICJBbGwgcm9hZHMgbGVhZCB0byBSb21lLi4iDQoNCioqKi4uLiBidXQgbm90IGFsbCByb2FkcyBhcmUgZWFzeSB0byB0cmF2ZWwuKioqDQoNCkRlcGVuZGluZyBvbiBmb3JtYXQsIGRhdGEgZmlsZXMgY2FuIGJlIG9wZW5lZCBpbiBhIG51bWJlciBvZiB3YXlzLiBUaGUgc2ltcGxlc3QgbWV0aG9kcyB3ZSB3aWxsIHVzZSBpbnZvbHZlIHRoZSBgcmVhZHJgIHBhY2thZ2UgYXMgcGFydCBvZiB0aGUgYHRpZHl2ZXJzZWAuIFRoZXNlIGZ1bmN0aW9ucyBoYXZlIGFscmVhZHkgYmVlbiBkZXZlbG9wZWQgdG8gc2ltcGxpZnkgdGhlIGltcG9ydCBwcm9jZXNzIGZvciB1c2Vycy4gVGhlIGZ1bmN0aW9ucyB3ZSB3aWxsIHVzZSBtb3N0IG9mdGVuIGFyZToNCg0KLSAgIFJlYWQgaW4gYSBkZWxpbWl0ZWQgZmlsZTogYHJlYWRfZGVsaW0oKWAsIGByZWFkX2NzdigpYCwgYHJlYWRfdHN2KClgLCBgcmVhZF9jc3YyKClgIFtFdXJvcGVhbiBkYXRhc2V0c10NCg0KLSAgIFJlYWQgaW4gZnJvbSBhIGZpbGUsIGxpbmUgYnkgbGluZTogYHJlYWRfbGluZXMoKWANCg0KTGV0J3MgcmVhZCBpbiBvdXIgZmlyc3QgZGF0YXNldCBzbyB0aGF0IHdlIGNhbiBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdC4NCg0KYGBge3J9DQojIFVzZSByZWFkX2NzdiB0byBsb29rIGF0IG91ciBQSFUgZGFpbHkgY2FzZSBkYXRhDQpjb3ZpZF9waHUuZGYgPC0gcmVhZF9jc3YoLi4uKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDaGVjayB0aGUgc3RydWN0dXJlIGFuZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgY292aWRfcGh1DQpzdHIoLi4uLCBnaXZlLmF0dHIgPSBGQUxTRSkNCmhlYWQoLi4uKQ0KdGFpbCguLi4pDQpgYGANCg0KYGBge3J9DQphbnkoaXMubmEoY292aWRfcGh1LmRmKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMS4xIE91ciBTQVJTLUNvVi0yIHB1YmxpYyBoZWFsdGggdW5pdCBkYXRhIGNvdmVycyAzNCByZWdpb25zDQoNCkZyb20gbG9va2luZyBhdCBvdXIgZGF0YSBwdWJsaWMgaGVhbHRoIHVuaXQgZGF0YSwgd2UgY2FuIHNlZSB0aGF0IGl0IGJlZ2lucyB0cmFja2luZyBvbiAyMDIwLTAxLTIzIGFuZCBnb2VzIHVwIHVudGlsIDIwMjMtMDEtMjUuIFRoYXQncyBvdmVyIDMgeWVhcnMgZm9yIGFueW9uZSBrZWVwaW5nIHRyYWNrISBJbiB0b3RhbCB0aGVyZSBhcmUgb2JzZXJ2YXRpb25zIGZvciAxLDA5OSBkYXlzIGFjcm9zcyAzNCBwdWJsaWMgaGVhbHRoIHVuaXRzLiBUaGUgZmluYWwgY29sdW1uIGFwcGVhcnMgdG8gYmUgYSB0YWxseSBydW5uaW5nIGZvciB0b3RhbCBjYXNlcyByZXBvcnRlZCBvbiB0aGF0IGRhdGUuIEFsdGhvdWdoIHRoZSBkYXRhIG5vIGxvbmdlciByZXByZXNlbnRzIGEgc2VtaS1hY2N1cmF0ZSBhY2NvdW50aW5nIG9mIHRoZSBzdGF0ZSBvZiBjYXNlIHJlcG9ydGluZyBpbiBPbnRhcmlvLCBpdCB3aWxsIHNlcnZlIChmb3Igbm93KSBhcyBhIGdvb2QgcmVmcmVzaGVyIG9uIGhvdyB0byBkbyBiYXNpYyBkYXRhIHdyYW5nbGluZy4NCg0KRnJvbSB0aGUgb3V0c2V0LCB3ZSBjYW4gc2VlIHRoZXJlIGFyZSBzb21lIGlzc3VlcyB3aXRoIHRoZSBkYXRhIHNldCB0aGF0IHdlJ2xsIHdhbnQgdG8gcmVzb2x2ZSBhbmQgd2UnbGwgd29yayB0aHJvdWdoIHNvbWUgYHRpZHl2ZXJzZWAgZnVuY3Rpb25zIGluIG9yZGVyIHRvIGRvIHRoYXQuIEZpcnN0IGxldCdzIHF1aWNrbHkgcmV2aWV3IHNvbWUgb2YgdGhlIHBvdGVudGlhbCBwcm9ibGVtcyB3aXRoIG91ciBkYXRhc2V0Lg0KDQoxLiAgVGhlcmUgYXJlIDM0IHB1YmxpYyBoZWFsdGggdW5pdHMgYW5kIGEgdG90YWwgY291bnQgZm9yIGVhY2ggZGF0ZS4gSXQgaXMgcHJlZmVyYWJsZSBmb3IgZGF0YSB2aXN1YWxpemF0aW9uIHRvIGNvbGxhcHNlIGFsbCBvZiB0aG9zZSBwdWJsaWMgaGVhbHRoIHVuaXRzIGludG8gYSBzaW5nbGUgdmFyaWFibGUgc28gdGhhdCB3ZSBoYXZlIGEgc2luZ2xlIHZhbHVlIGBuZXdfY2FzZXNgIGZvciBlYWNoIGBEYXRlYCBvYnNlcnZhdGlvbi4gQXQgdGhlIHNhbWUgdGltZSB3ZSB3aWxsIG5vdCBjb2xsYXBzZSBgVG90YWxgIGludG8gdGhhdCBzYW1lIHZhcmlhYmxlLg0KDQoyLiAgVGhlIGRhdGEgaXMgcmlmZSB3aXRoIGBOQWAgdmFsdWVzLiBNYW55IGluc3RhbmNlIGFyZSBsaWtlbHkgZHVlIHRvIG5vIGRhdGEgYmVpbmcgY29sbGVjdGVkIG9uIHRob3NlIGRhdGVzLiBGb3Igb3VyIHB1cnBvc2VzLCBpdCBtYXkgYmUgc2ltcGxlciB0byByZXBsYWNlIHRoZW0gd2l0aCBhIHZhbHVlIG9mIDAuDQoNCjMuICBPdXIgcHVibGljIGhlYWx0aCB1bml0IG5hbWVzIGFyZSBhIGxpdHRsZSBjbHVua3kuIFdlIHNob3VsZCB0cmltIHRoZW0gZG93biB0byBzaW1wbGVyIHJlZ2lvbiBuYW1lcy4NCg0KSW4gdGhlIGVuZCwgd2Ugd2FudCB0byBjb252ZXJ0IG91ciBkYXRhIHRvIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczoNCg0KfCBkYXRlIFw8ZGF0ZVw+IHwgdG90YWxfcGh1X25ldyBcPGRibFw+IHwgcHVibGljX2hlYWx0aF91bml0IFw8ZmN0XD4gfCBuZXdfY2FzZXMgXDxkYmxcPiB8DQp8Oi0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS06fA0KfCAgMjAyMC0wMS0yMyAgIHwgICAgICAgICAgIDAgICAgICAgICAgIHwgICAgICAgICAgIEFsZ29tYSAgICAgICAgICAgfCAgICAgICAgIDAgICAgICAgICB8DQp8ICAyMDIwLTAxLTIzICAgfCAgICAgICAgICAgMCAgICAgICAgICAgfCAgICAgICAgQnJhbnRfQ291bnR5ICAgICAgICB8ICAgICAgICAgMCAgICAgICAgIHwNCnwgIDIwMjAtMDEtMjMgICB8ICAgICAgICAgICAwICAgICAgICAgICB8ICAgICAgICBDaGF0aGFtX0tlbnQgICAgICAgIHwgICAgICAgICAwICAgICAgICAgfA0KfCAgICAgIC4uLiAgICAgIHwgICAgICAgICAgLi4uICAgICAgICAgIHwgICAgICAgICAgICAuLi4gICAgICAgICAgICAgfCAgICAgICAgLi4uICAgICAgICB8DQoNCkJlZm9yZSB3ZSB0YWNrbGUgdGhlc2UgaXNzdWVzLCBsZXQncyBnbyBhaGVhZCBhbmQgcmV2aWV3IHNvbWUgb2YgdGhlIHRvb2xzIGF0IG91ciBkaXNwb3NhbC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDMuMi4wIFRoZSBgdGlkeXZlcnNlYCBwYWNrYWdlIGFuZCBpdCdzIGNvbnRlbnRzIG1ha2UgbWFuaXB1bGF0aW5nIGRhdGEgZWFzaWVyDQoNCldoaWxlIHRoZSB0aWR5dmVyc2UgaXMgY29tcG9zZWQgb2YgbXVsdGlwbGUgcGFja2FnZXMsIHdlIHdpbGwgYmUgZm9jdXNlZCBvbiB3b3JraW5nIHdpdGggYSBzdWJzZXQgb2YgdGhlc2U6IGBkcGx5cmAsIGB0aWR5cmAsIGFuZCBgc3RyaW5ncmAuDQoNCiMjIyAzLjIuMC4xIFJlZGlyZWN0IHlvdXIgb3V0cHV0IHdpdGggYCU+JWAgd2hlbmV2ZXIgeW91IGNhbiENCg0KVG8gc2F2ZSBvbiBtYWtpbmcgZXh0cmEgdmFyaWFibGVzIGluIG1lbW9yeSBhbmQgdG8gaGVscCBtYWtlIG91ciBjb2RlIG1vcmUgY29uY2lzZSwgd2Ugc2hvdWxkIHVzZSBvZiB0aGUgYCU+JWAgc3ltYm9sLiBUaGlzIGlzIGEgcmVkaXJlY3Rpb24gb3IgcGlwZSBzeW1ib2wgc2ltaWxhciB0byB0aGUgYHxgIGluIFVuaXggb3BlcmF0aW5nIHN5c3RlbXMgYW5kIGlzIHVzZWQgZm9yIHJlZGlyZWN0aW5nIG91dHB1dCBmcm9tIG9uZSBmdW5jdGlvbiB0byB0aGUgaW5wdXQgb2YgYW5vdGhlci4gQnkgdGhvdWdodGZ1bGx5IGNvbWJpbmluZyB0aGlzIHdpdGggb3RoZXIgY29tbWFuZHMsIHdlIGNhbiBhbHRlciBvciBxdWVyeSBvdXIgZGF0YXNldHMgd2l0aCBlYXNlLg0KDQpXZSdsbCBhbHNvIGludHJvZHVjZSB0aGUgYCU8PiVgIGluIHRoaXMgY2xhc3MuIFRoaXMgaXMgYSBsaXR0bGUgbW9yZSBhZHZhbmNlZCBidXQgaXQgYWxsb3dzIHVzIHRvIGFzc2lnbiB0aGUgZmluYWwgcHJvZHVjdCBvZiBvdXIgY2hhaW4gb2YgY29tbWFuZHMgdG8gdGhlIHZlcnkgZmlyc3Qgb2JqZWN0Lg0KDQpXaGVuZXZlciB3ZSBhcmUgcmVkaXJlY3RpbmcsIHdlIGFyZSBpbXBsaWNpdGx5IHBhc3Npbmcgb3VyIG91dHB1dCB0byB0aGUgZmlyc3QgcGFyYW1ldGVyIG9mIHRoZSBuZXh0IGZ1bmN0aW9uLiBXZSBtYXkgbm90IGFsd2F5cyB3YW50IHRvIHVzZSB0aGUgZW50aXJldHkgb2YgdGhlIG91dHB1dCBvciB3ZSBtYXkgd2FudCB0byBhbHNvIHJldXNlIHRoYXQgcmVkaXJlY3RlZCBvdXRwdXQgYXMgcGFydCBvZiBhbm90aGVyIHBhcmFtZXRlci4gVG8gZG8gc28gd2UgY2FuIHVzZSBgLmAgdG8gZXhwbGljaXRseSBkZW5vdGUgdGhlIHJlZGlyZWN0ZWQgb3V0cHV0Lg0KDQojIyMgMy4yLjAuMiBgZHBseXJgIGhhcyBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyBhbmQgYWx0ZXJpbmcgeW91ciBkYXRhDQoNCldlIHdpbGwgdXNlIHRoZSAidmVyYnMiIG9mIHRoZSBgZHBseXJgIGZ1bmN0aW9uIG9mdGVuIHRvIG1hc3NhZ2UgdGhlIGxvb2sgb2Ygb3VyIGRhdGEgYnkgY2hhbmdpbmcgY29sdW1uIG5hbWVzIG9yIHN1YnNldHRpbmcgaXQuIFRoZSBtb3N0IGNvbW1vbiB2ZXJicyB5b3Ugd2lsbCBzZWUgaW4gdGhpcyBjb3Vyc2UgYXJlLg0KDQp8IEZ1bmN0aW9uKHMpICAgICAgICAgICAgICAgICAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYGFycmFuZ2UoKWAgICAgICAgICAgICAgICAgICAgIHwgQXJyYW5naW5nIHJvd3MgYnkgY29sdW1uIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGNvdW50KClgLCBgdGFsbHkoKWAgICAgICAgICAgIHwgQ291bnRpbmcgb2JzZXJ2YXRpb25zIGJ5IGdyb3VwICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGRpc3RpbmN0KClgICAgICAgICAgICAgICAgICAgIHwgU3Vic2V0dGluZyByb3dzIGJ5IGRpc3RpbmN0IG9yIHVuaXF1ZSB2YWx1ZXMgICAgICAgICAgIHwNCnwgYGZpbHRlcigpYCAgICAgICAgICAgICAgICAgICAgIHwgU3Vic2V0dGluZyByb3dzIGJ5IGNvbHVtbiB2YWx1ZXMgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYG11dGF0ZSgpYCwgYHRyYW5zbXV0ZSgpYCAgICAgIHwgQ3JlYXRlLCBtb2RpZnksIG9yIGRlbGV0ZSBjb2x1bW5zICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHNlbGVjdCgpYCAgICAgICAgICAgICAgICAgICAgIHwgU3Vic2V0IGNvbHVtbnMgdXNpbmcgdGhlaXIgbmFtZXMgYW5kIHR5cGVzICAgICAgICAgICAgIHwNCnwgYHN1bW1hcml6ZSgpYCBvciBgc3VtbWFyaXNlKClgIHwgU3VtbWFyaXplIGJ5IGdyb3VwcyB0byBmZXdlciByb3dzICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGdyb3VwX2J5KClgIHZzLiBgdW5ncm91cCgpYCAgIHwgZ3JvdXAgYnkgb25lIG9yIG1vcmUgdmFyaWFibGVzICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHJvd3dpc2UoKWAgICAgICAgICAgICAgICAgICAgIHwgZ3JvdXAgZGF0YSBhcyBzaW5nbGUgcm93cyBmb3IgY2FsY3VsYXRpb25zIGFjcm9zcyBlYWNoIHwNCnwgYHJlbmFtZSgpYCwgYW5kIGByZWxvY2F0ZSgpYCAgIHwgUmVuYW1lIG9yIG1vdmUgY29sdW1ucyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KIyMjIDMuMi4wLjMgYHRpZHlyYCBoYXMgYWRkaXRpb25hbCBmdW5jdGlvbnMgZm9yIHJlc2hhcGluZyBvdXIgZGF0YQ0KDQpUaGUgYHRpZHlyYCBwYWNrYWdlIHdpbGwgYmUgbW9zdCB1c2VmdWwgd2hlbiB3ZSBhcmUgdHJ5aW5nIHRvIHJlc2hhcGUgb3VyIGRhdGEgZnJvbSB0aGUgd2lkZSB0byB0aGUgbG9uZyBmb3JtYXQgb3IgKnZpY2UgdmVyc2EqLiBUaGlzIGlzIG11Y2ggbW9yZSB1c2VmdWwgZm9yIHdoZW4gd2Ugd2FudCB0byBkcmFzdGljYWxseSBhbHRlciBwb3J0aW9ucyBvciBhbGwgb2Ygb3VyIGRhdGEuDQoNCnwgRnVuY3Rpb24ocykgICAgICB8IERlc2NyaXB0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYHBpdm90X2xvbmdlcigpYCB8IFBpdm90IGRhdGEgZnJvbSB3aWRlIHRvIGxvbmcgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHBpdm90X3dpZGVyKClgICB8IFBpdm90IGRhdGEgZnJvbSBsb25nIHRvIHdpZGUgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGV4dHJhY3QoKWAgICAgICB8IEV4dHJhY3QgYSBjaGFyYWN0ZXIgY29sdW1uIGludG8gbXVsdGlwbGUgZ3JvdXBzICAgIHwNCnwgYHNlcGFyYXRlKClgICAgICB8IFNlcGFyYXRlIGEgY2hhcmFjdGVyIGNvbHVtbiBpbnRvIG11bHRpcGxlIGdyb3VwcyAgIHwNCnwgYHVuaXRlKClgICAgICAgICB8IFVuaXRlIG11bHRpcGxlIGNvbHVtbnMgaW50byBvbmUgYnkgcGFzdGluZyBzdHJpbmdzIHwNCnwgYGRyb3BfbmEoKWAgICAgICB8IERyb3Agcm93cyBjb250YWluaW5nIG1pc3NpbmcgdmFsdWVzICAgICAgICAgICAgICAgIHwNCnwgYHJlcGxhY2VfbmEoKWAgICB8IFJlcGxhY2UgTkFzIHdpdGggc3BlY2lmaWMgdmFsdWVzICAgICAgICAgICAgICAgICAgIHwNCg0KIyMjIDMuMi4wLjQgYHN0cmluZ3JgIHByb3ZpZGVzIGZ1bmN0aW9uYWxpdHkgZm9yIHNlYXJjaGluZyBkYXRhIGJhc2VkIG9uIHJlZ3VsYXIgZXhwcmVzc2lvbnMNCg0KVGhlIGBzdHJpbmdyYCBwYWNrYWdlIHdpbGwgY29tZSBpbiBtb3N0IHVzZWZ1bCB3aGVuIHdlIGFyZSB0cnlpbmcgdG8gZml4IHN0cmluZyBpc3N1ZXMgd2l0aCBvdXIgZGF0YS4gTWFueSB0aW1lIG91ciBoZWFkZXJzIG9yIGRhdGEgd2lsbCBjb250YWluIHNwYWNlcyBvciBwb29yIGZvcm1hdHRpbmcuIE1hbnkgdGltZXMgd2Ugd2lsbCBwcmVmZXIgdG8gaGF2ZSBvdXIgaGVhZGVycyBpbiBsb3dlciBjYXNlIGZvcm1hdCwgd2l0aCBhbnkgc3BhY2VzIHJlcGxhY2VkIGJ5IGFuIGBfYC4gV2UnbGwgYWxzbyB1c2UgdmVyYnMgZnJvbSB0aGlzIHBhY2thZ2UgdG8gbWFrZSBhbnkgdmFyaWFibGVzIG9yIGRhdGEgbW9yZSBjb25jaXNlLg0KDQp8IENhdGVnb3J5ICAgICAgICAgICAgICB8IEZ1bmN0aW9uKHMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICoqU3RyaW5nIGFuYWx5c2lzKiogICB8IGBzdHJfY291bnQoKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDb3VudCB0aGUgbnVtYmVyIG9mIG1hdGNoZXMgaW4gYSBzdHJpbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAqKlN0cmluZyByZXRyaWV2YWwqKiAgfCBgc3RyX2RldGVjdCgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRGV0ZWN0IHRoZSBwcmVzZW5jZSAob3IgYWJzZW5jZSkgb2YgYSBwYXR0ZXJuIGluIHN0cmluZyAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICB8IGBzdHJfZXh0cmFjdCgpYCBhbmQgYHN0cl9leHRyYWN0X2FsbCgpYCAgICAgICAgICAgICAgICAgfCBFeHRyYWN0IG1hdGNoaW5nIHBhdHRlcm5zIGZyb20gYSBzdHJpbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgIHwgYHN0cl9tYXRjaCgpYCBhbmQgYHN0cl9tYXRjaF9hbGwoKWAgICAgICAgICAgICAgICAgICAgICB8IEV4dHJhY3QgbWF0Y2hlZCBncm91cHMgZnJvbSBhIHN0cmluZyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgfCBgc3RyX3N1YnNldCgpYCBhbmQgYHN0cl93aGljaCgpYCAgICAgICAgICAgICAgICAgICAgICAgIHwgS2VlcCBvciBmaW5kIHN0cmluZ3MgbWF0Y2hpbmcgYSBwYXR0ZXJuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgKipTdHJpbmcgYWx0ZXJhdGlvbioqIHwgYHN0cl9yZW1vdmUoKWAgYW5kIGBzdHJfcmVtb3ZlX2FsbCgpYCAgICAgICAgICAgICAgICAgICB8IFJlbW92ZSBtYXRjaGVkIHBhdHRlcm5zIGZyb20gYSBzdHJpbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgfCBgc3RyX3NwbGl0KClgLCBgc3RyX3NwbGl0X2ZpeGVkKClgLCBhbmQgYHN0cl9zcGxpdF9uKClgIHwgU3BsaXQgYSBzdHJpbmcgaW50byBwaWVjZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICB8IGBzdHJfYygpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDb25jYXRlbmF0ZSBtdWx0aXBsZSBzdHJpbmdzIGludG8gYSBzaW5nbGUgc3RyaW5nIHdpdGggb3B0aW9uYWwgc2VwYXJhdG9yIHwNCnwgICAgICAgICAgICAgICAgICAgICAgIHwgYHN0cl9mbGF0dGVuKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEZsYXR0ZW4gYSBzdHJpbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgfCBgc3RyX3N1YigpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRXh0cmFjdCBhbmQgcmVwbGFjZSBzdWJzdHJpbmdzIGZyb20gYSBjaGFyYWN0ZXIgdmVjdG9yICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICB8IGBzdHJfdG9fdXBwZXIoKWAgYW5kIGBzdHJfdG9fbG93ZXIoKWAgICAgICAgICAgICAgICAgICAgfCBDb252ZXJ0IGNhc2Ugb2YgYSBzdHJpbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9Hb1RfRXhhbXBsZXNfY29taW5nLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPiBUaW1lIHRvIHRhY2tsZSBvdXIgZGF0YXNldCENCjo6Og0KDQojIyMgMy4yLjEgUmVmb3JtYXQgb3VyIHdpZGUgdGFibGUgd2l0aCBgcGl2b3RfbG9uZ2VyKClgDQoNCkFzIHlvdSBtYXkgcmVjYWxsLCBvdXIgUEhVIGRhdGEgaXMgZm9ybWF0dGVkIHN1Y2ggdGhhdCBlYWNoIGNvbHVtbiByZXByZXNlbnRzIG5ldyBjYXNlcyBwZXIgZGF5IGZvciBhIHNpbmdsZSBQSFUuIEl0J3MgYSBncmVhdCB3YXkgdG8gZm9ybWF0IGZvciBkYXRhIGVudHJ5IGFuZCBjZXJ0YWlubHkgcmVkdWNlcyBvbiByZWR1bmRhbmN5LiBIb3dldmVyLCBmb3IgdXMgdG8gd29yayB3aXRoIHRoaXMgZGF0YSwgd2Ugd2FudCB0byBjb2xsYXBzZSBhbGwgb2YgdGhvc2UgUEhVcyBpbnRvIGEgc2luZ2xlIGNvbHVtbi4NCg0KVG9kYXkgd2Ugd2lsbCB1c2UgdGhlIGBwaXZvdF9sb25nZXIoKWAgZnVuY3Rpb24gdG8gY29udmVydCBvdXIgd2lkZS1mb3JtYXQgZGF0YSBvdmVyIHRvIGxvbmctZm9ybWF0LiBGb3Igb3VyIHB1cnBvc2VzLCB3ZSB3aWxsIHJlbHkgb24gZm91ciBwYXJhbWV0ZXJzOiAxLiBgZGF0YWA6IHRoZSBkYXRhIGZyYW1lIChhbmQgY29sdW1ucykgdGhhdCB3ZSB3aXNoIHRvIHRyYW5zZm9ybS4gMi4gYGNvbHNgOiB0aGUgY29sdW1ucyB0aGF0IHdlIHdpc2ggdG8gZ2F0aGVyL2NvbGxhcHNlIGludG8gYSBsb25nIGZvcm1hdC4gMy4gYG5hbWVzX3RvYDogdGhlIHZhcmlhYmxlIG5hbWUgb2YgdGhlICpuZXcqIGNvbHVtbiB0byBob2xkIHRoZSBjb2xsYXBzZWQgaW5mb3JtYXRpb24gZnJvbSBvdXIgY3VycmVudCBjb2x1bW5zLiA0LiBgdmFsdWVzX3RvYDogVGhlIHZhcmlhYmxlIG5hbWUgb2YgdGhlIHZhbHVlcyBmb3IgZWFjaCBvYnNlcnZhdGlvbiB0aGF0IHdlIGFyZSBjb2xsYXBzaW5nIGRvd24uDQoNCldlJ2xsIGJlIHVzaW5nIGEgc2VyaWVzIG9mIGAlPiVgIHNvIGZvciBub3cgd2Ugd29uJ3Qgc2F2ZSBvdXIgd29yayB0byBhIG5ldyBvYmplY3QuDQoNCmBgYHtyfQ0KIyBBIHJlbWluZGVyIG9mIHdoYXQgb3VyIGRhdGEgbG9va3MgbGlrZQ0KY292aWRfcGh1LmRmICU+JSBoZWFkKCkNCmBgYA0KDQpgYGB7cn0NCiMgU3RhcnQgd2l0aCBvdXIgd2lkZS1mb3JtYXQgcGh1IGRhdGENCmNvdmlkX3BodS5kZiAlPiUgDQoNCiMgUGl2b3QgdGhlIGRhdGEgaW50byBhIGxvbmctZm9ybWF0IHNldA0KcGl2b3RfbG9uZ2VyKGNvbHMgPSAuLi4sIG5hbWVzX3RvID0gLi4uLCB2YWx1ZXNfdG8gPSAuLi4pICU+JSANCg0KIyBKdXN0IHRha2UgYSBxdWljayBsb29rIGF0IHRoZSBvdXRwdXQuDQpzdHIoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4yLjIgUmVwbGFjZSBgTkFgIHZhbHVlcyBmcm9tIG91ciBkYXRhIHdpdGggYHJlcGxhY2VfbmEoKWANCg0KT3VyIGNvbnZlcnNpb24gdG8gbG9uZyBmb3JtYXQgY3JlYXRlcyAzNywzNjYgb2JzZXJ2YXRpb25zIHJlbGF0aW5nIGEgYERhdGVgIHRvIGEgYG5ld19jYXNlc2AgdmFsdWUgaW4gYSBzcGVjaWZpYyBgUHVibGljX0hlYWx0aF9Vbml0YCAob3IgdG90YWwpLiBGcm9tIHRoZSBsb29rcyBvZiBvdXIgZGF0YSwgaG93ZXZlciwgd2UgZG8gaGF2ZSBgTkFgIHZhbHVlcyB1bmRlciBvdXIgYG5ld19jYXNlc2AgdmFyaWFibGUuDQoNCldlIGhhdmUgdHdvIG9wdGlvbnM6IDEuIFJlbW92ZSB0aGUgYE5BYCBvYnNlcnZhdGlvbnMgZnJvbSBvdXIgZGF0YSBzZXQuIFRoZXJlIHdvbid0IGJlIGFueSBsb3NzIG9mIGluZm9ybWF0aW9uIHNpbmNlIHdlIGNvdWxkIHJlYnVpbGQgdGhlIG9yaWdpbmFsIGRhdGEgaWYgd2UgcmVhbGx5IG5lZWRlZCB0by4gMi4gUmVwbGFjZSB0aGUgYE5BYCBvYnNlcnZhdGlvbnMgd2l0aCBhIHZhbHVlIHRoYXQgbWFrZXMgc2Vuc2UgZm9yIG91ciBhbmFseXNpcy4NCg0KTGV0J3MgcmVwbGFjZSB0aGUgbWlzc2luZyBvYnNlcnZhdGlvbnMgd2l0aCBhIG5ldyB2YWx1ZSwgMCwgdXNpbmcgYHJlcGxhY2VfbmEoKWAuIFRoaXMgZnVuY3Rpb24gd2lsbCBuZWVkIHR3byBwYXJhbWV0ZXJzOg0KDQoxLiAgYGRhdGFgOiB0aGUgZGF0YSBmcmFtZSBvciB2ZWN0b3IgdGhhdCBpdCB3aWxsIHNjYW4gZm9yIGBOQWAgdmFsdWVzLg0KMi4gIGByZXBsYWNlYDogdGhlIHZhbHVlIHRoYXQgd2Ugd2lsbCB1c2UgdG8gcmVwbGFjZSBgTkFgLg0KDQpXZSdyZSBnb2luZyB0byB1cGRhdGUgb3VyIHBpcGUgb2YgY29tbWFuZHMgYW5kIHNhdmUgdGhlIGZpbmFsIG91dHB1dCBpbnRvIGEgbmV3IHZhcmlhYmxlIGBjb3ZpZF9waHVfbG9uZy5kZmAuDQoNCmBgYHtyfQ0KIyBQaXZvdCB0aGUgZGF0YSBpbnRvIGEgbG9uZy1mb3JtYXQgc2V0IGFuZCByZW1vdmUgTkFzIGZyb20gdGhlIHZhbHVlIHRhYmxlDQpjb3ZpZF9waHVfbG9uZy5kZiA8LSANCiAgICBjb3ZpZF9waHUuZGYgJT4lIA0KICAgIHBpdm90X2xvbmdlcihjb2xzID0gYygyOjM1KSwgbmFtZXNfdG8gPSAicHVibGljX2hlYWx0aF91bml0IiwgdmFsdWVzX3RvID0gIm5ld19jYXNlcyIpICAlPiUNCg0KICAgICMjIyBDaGFuZ2UgdGhlIHZhbHVlcyBvZiAibmV3X2Nhc2VzIiB1c2luZyB0aGUgbXV0YXRlIGZ1bmN0aW9uDQogICAgbXV0YXRlKG5ld19jYXNlcyA9IC4uLikNCmBgYA0KDQpgYGB7cn0NCiMgQ2hlY2sgdGhhdCB3ZSBoYXZlIGNvdmVyZWQgYWxsIG9mIHRoZSBOQSB2YWx1ZXMgaW4gb3VyIGRhdGEgZnJhbWUgYnkgbG9va2luZyBmb3IgY29tcGxldGUgY2FzZXMNCm5yb3coY292aWRfcGh1X2xvbmcuZGZbY29tcGxldGUuY2FzZXMoY292aWRfcGh1X2xvbmcuZGYpLF0pDQoNCiMgT3IganVzdCBjaGVjayBmb3IgTkEgdmFsdWVzDQphbnkoaXMubmEoY292aWRfcGh1X2xvbmcuZGYpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUYWtlIGEgbG9vayBhdCB0aGUgUHVibGljIEhlYWx0aCBVbml0IG5hbWVzDQpwcmludCguLi4pDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjIuMyBSZWZvcm1hdCBvdXIgcHVibGljIGhlYWx0aCB1bml0IG5hbWVzIHdpdGggYHN0cl9yZXBsYWNlX2FsbCgpYA0KDQpMb29raW5nIGF0IG91ciBQSFUgbmFtZXMsIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBhIGxvdCBvZiByZWR1bmRhbmN5IGluIG91ciBuYW1lcy4gV2Ugc2VlIHRoZXkgbWF5IHNvbWV0aW1lcyBlbmQgb3IgYmVnaW4gaW4gc29tZSBmb3JtIG9mOiAtIFxfRGlzdHJpY3QgLSBcX1JlZ2lvbiAtIFxfQ2l0eSAtICpDb3VudHkgLSBDaXR5X29mKg0KDQpXZSBoYXZlIGEgY291cGxlIG9mIGNob2ljZXMgYnV0IHdlIGNhbiBlaXRoZXIgdXNlIGBzdHJfcmVwbGFjZV9hbGwoKWAgb3IgYSBzcGVjaWZpYyB2ZXJzaW9uIG9mIHRoYXQsIGBzdHJfcmVtb3ZlX2FsbCgpYCwgd2hpY2ggc2ltcGx5IHJlcGxhY2VzIGEgcGF0dGVybiB3aXRoIGFuIGVtcHR5IGNoYXJhY3Rlci4NCg0KRm9yIGBzdHJfcmVwbGFjZV9hbGwoKWAgd2Ugd2lsbCBzdXBwbHk6IDEuIGBzdHJpbmdgOiBhIHNpbmdsZSBzdHJpbmcgb3IgdmVjdG9yIG9mIHN0cmluZ3MuIDIuIGBwYXR0ZXJuYDogdGhlIHBhdHRlcm4gd2Ugd2lzaCB0byBzZWFyY2ggZm9yIGluIHRoZSBmb3JtIG9mIGEgc3RyaW5nIG9yIHJlZ3VsYXIgZXhwcmVzc2lvbi4gMy4gYHJlcGxhY2VgOiB0aGUgcmVwbGFjZW1lbnQgc3RyaW5nIHdlIHdpc2ggdG8gdXNlLg0KDQpGb3IgdGhlIHB1cnBvc2VzIG9mIG91ciB2aXN1YWxpemF0aW9uIGFuZCBub3cgdGhhdCB0aGVzZSBhcmUgbm93IGxvbmdlciBjb2x1bW4gbmFtZXMsIHdlIHdpbGwgcmVwbGFjZSBhbGwgcmVtYWluaW5nIHVuZGVyc2NvcmUgKGBfYCkgY2hhcmFjdGVycyB3aXRoIGEgc3BhY2UuIFRvIHdyYXAgdGhhdCB1cCB3ZSdsbCBjb252ZXJ0IG91ciB1cGRhdGVkIHZhcmlhYmxlIHRvIGEgZmFjdG9yIGFuZCBvdmVyd3JpdGUgb3VyIG9yaWdpbmFsIGBjb3ZpZF9waHVfbG9uZy5kZmAuDQoNCldlIHdpbGwgYWNjb21wbGlzaCB0aGlzIGFsbCB0aHJvdWdoIG11bHRpcGxlIGNhbGxzIHRvIG11dGF0ZS4NCg0KYGBge3J9DQojIENsZWFuIHVwIHRoZSBQdWJsaWMgSGVhbHRoIFVuaXQgbmFtZXMNCmNvdmlkX3BodV9sb25nLmRmICAlPD4lIA0KDQojIFJlcGxhY2VzIG91ciBwdWJsaWNfaGVhbHRoX3VuaXQgdmFsdWVzIHdpdGggb25lcyB3aGVyZSB3ZSByZW1vdmUgZXhjZXNzIHZlcmJhZ2UNCm11dGF0ZShwdWJsaWNfaGVhbHRoX3VuaXQgPSBzdHJfcmVwbGFjZV9hbGwoc3RyaW5nID0gLi4uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IC4uLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZSA9ICIiKSkgJT4lIA0KDQojIEZyb20gdGhlIHVwZGF0ZWQgdmVyc2lvbiBvZiBwdWJsaWNfaGVhbHRoX3VuaXQsIHJlcGxhY2UgYWxsIF8gd2l0aCBhICIgIg0KbXV0YXRlKHB1YmxpY19oZWFsdGhfdW5pdCA9IHN0cl9yZXBsYWNlX2FsbChzdHJpbmcgPSAuLi4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIl8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gIiAiKSkgJT4lIA0KDQojIE5vdyBtYWtlIHN1cmUgdGhhdCBpdCdzIGEgZmFjdG9yIGZvciBsYXRlcg0KbXV0YXRlKHB1YmxpY19oZWFsdGhfdW5pdCA9IGFzLmZhY3RvcihwdWJsaWNfaGVhbHRoX3VuaXQpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUYWtlIGEgbG9vayBhdCB0aGUgbmV3IHNldCBvZiBwaHUgbmFtZXMNCnByaW50KGxldmVscyhjb3ZpZF9waHVfbG9uZy5kZiRwdWJsaWNfaGVhbHRoX3VuaXQpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUYWtlIGEgcXVpY2sgbG9vayBhdCBvdXIgZmluYWwgZGF0YXNldA0KaGVhZChjb3ZpZF9waHVfbG9uZy5kZikNCg0KIyBNYWtlIGEgcXVpY2sgY29weSBoZXJlIHRvbw0KY292aWRfcGh1X2xvbmdfY29weS5kZiA9IGNvdmlkX3BodV9sb25nLmRmDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjIuNCBgcmVuYW1lKClgIHZhcmlhYmxlcyBmb3IgY2xhcml0eQ0KDQpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBiYXNpYyBzdHJ1Y3R1cmUgZm9yIG91ciBkYXRhLCB3ZSB3YW50IHRvIGNsZWFuIGl0IHVwIGp1c3QgYSBsaXR0bGUgYml0IGJ5IHJlbmFtaW5nIG91ciBgVG90YWxgIGNvbHVtbiB0byBjbGFyaWZ5IHRoYXQgaXQgcmVwcmVzZW50cyB0b3RhbCBuZXcgY2FzZXMgYWNyb3NzIGFsbCBQSFVzIGZvciB0aGF0IGRhdGUuIFdoeSBkaWQgd2Uga2VlcCB0aGlzIGNvbHVtbiBzZXBhcmF0ZT8gTm93IHdlIGNhbiB1c2UgdGhpcyBpbmZvcm1hdGlvbiB0byBnZW5lcmF0ZSBwZXJjZW50YWdlIHRvdGFscyBmb3IgZWFjaCBQSFUgaWYgd2UgY2hvb3NlIHRvLiBXZSdsbCBhbHNvIGNoYW5nZSBvdXIgYERhdGVgIGNvbHVtbiB0byBsb3dlciBjYXNlIGF0IHRoZSBzYW1lIHRpbWUuDQoNCldlJ2xsIHVzZSBgcmVuYW1lKClgIGZyb20gYGRwbHlyYCB0byBhY2NvbXBsaXNoIHRoZSB0YXNrIG9mIHJlbmFtaW5nIG91ciBjb2x1bW4uIFRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHlvdSBjb3VsZCBhY2NvbXBsaXNoIHRoaXMgd2l0aG91dCB1c2luZyBgZHBseXJgIGJ1dCB0aGUgc2ltcGxpY2l0eSBvZiBpdCBpcyBuaWNlLg0KDQpgYGB7cn0NCiMgUmVuYW1lIG91ciBUb3RhbCBjb2x1bW4gdG8gY2xhcmlmeSBpdCdzIG1lYW5pbmcNCmNvdmlkX3BodV9sb25nLmRmICAlPiUgDQpyZW5hbWUoLi4uID0gVG90YWwsDQogICAgICAgLi4uID0gRGF0ZSkgJT4lIA0KaGVhZCgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjIuNSBSZW9yZGVyIHlvdXIgY29sdW1ucyB3aXRoIGByZWxvY2F0ZSgpYA0KDQpUaGUgbGFzdCBjbGVhbnVwIHdlIGNhbiBhY2NvbXBsaXNoIHdpdGggb3VyIGRhdGEgaXMgdG8gbW92ZSBgdG90YWxfcGh1X25ld2AgdG8gdGhlIGxhc3QgY29sdW1uIG9mIG91ciBkYXRhIGZyYW1lLiBUaGlzIGlzIGZvciBwZXJzb25hbCBwcmVmZXJlbmNlIGJ1dCBhbHNvIG1ha2VzIG1vcmUgc2Vuc2Ugd2hlbiBzaW1wbHkgbG9va2luZyBhdCB0aGUgZGF0YS4gVGhlIGByZWxvY2F0ZSgpYCB2ZXJiIGZyb20gYGRwbHlyYCBhY2NvbXBsaXNoZXMgdGhpcyB3aXRoIGVhc2Ugc2luY2Ugd2UgYXJlIG5vdCBkcm9wcGluZyBvciByZW1vdmluZyBjb2x1bW5zLiBJdCB1c2VzIHNvbWUgZXh0cmEgc3ludGF4IHRvIGhlbHAgYWNjb21wbGlzaCBpdHMgZnVuY3Rpb25zOg0KDQoxLiAgYC5kYXRhYDogdGhlIGRhdGEgZnJhbWUgb3IgdGliYmxlIHdlIHdhbnQgdG8gYWx0ZXINCjIuICBgLi4uYDogdGhlIGNvbHVtbnMgd2Ugd2lzaCB0byBtb3ZlDQozLiAgYC5iZWZvcmVgIG9yIGAuYWZ0ZXJgOiBkZXRlcm1pbmVzIHRoZSBkZXN0aW5hdGlvbiBvZiB0aGUgY29sdW1ucy4gU3VwcGx5aW5nIG5laXRoZXIgd2lsbCBtb3ZlIGNvbHVtbnMgdG8gdGhlIGxlZnQtaGFuZCBzaWRlLg0KDQpJbiBmYWN0LCBgcmVsb2NhdGUoKWAgY2FuIGJlIHVzZWQgdG8gcmVuYW1lIGEgY29sdW1uIGFzIHdlbGwgYnV0IGl0IHdpbGwgYWxzbyBiZSBtb3ZlZCBieSBkZWZhdWx0IHNvIGNvbnNpZGVyIHRoZSByYW1pZmljYXRpb25zIG9mIHN1Y2ggYW4gYWN0aW9uIQ0KDQpOb3RlOiBXZSBjb3VsZCBhY2NvbXBsaXNoIGEgc2ltaWxhciByZXN1bHQgdXNpbmcgdGhlIGBzZWxlY3RgIGNvbW1hbmQgYXMgd2VsbC4gSXQncyByZWFsbHkgdXAgdG8gd2hhdCB5b3UncmUgY29tZm9ydGFibGUgd2l0aCBidXQgaXQgaXMgbXVjaCBzaW1wbGVyIHRvIHVzZSBgcmVsb2NhdGUoKWAgd2hlbiB5b3UgYXJlIHdvcmtpbmcgd2l0aCBhIGxhcmdlIG51bWJlciBvZiBjb2x1bW5zIGFuZCB5b3Ugd2FudCB0byBtb3ZlIG9uZSB0byBhIHNwZWNpZmljIGxvY2F0aW9uLg0KDQpgYGB7cn0NCiMgUmVuYW1lIG91ciBUb3RhbCBjb2x1bW4gdG8gY2xhcmlmeSBpdCdzIG1lYW5pbmcNCmNvdmlkX3BodV9sb25nLmRmICAlPD4lIA0KcmVuYW1lKHRvdGFsX3BodV9uZXcgPSBUb3RhbCwNCiAgICAgICBkYXRlID0gRGF0ZSkgJT4lIA0KIyByZWxvY2F0ZSBvdXIgdG90YWwgY29sdW1uIHRvIHRoZSByaWdodCBzaWRlDQpyZWxvY2F0ZSh0b3RhbF9waHVfbmV3LCAuLi4gPSBuZXdfY2FzZXMpDQoNCmhlYWQoY292aWRfcGh1X2xvbmcuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtZGFuZ2VyfQ0KPGI+Q29tcHJlaGVuc2lvbiBRdWVzdGlvbiAzLjIuNTo8L2I+IEluIHRoZSBhYm92ZSBleGFtcGxlIHdlIHVzZWQgdGhlIDxiPnJlbG9jYXRlKCk8L2I+IGZ1bmN0aW9uIHRvIG1vdmUgdGhlIDxiPiJ0b3RhbF9waHVfbmV3IjwvYj4gY29sdW1uIHRvIHRoZSBlbmQgb2Ygb3VyIGRhdGEgZnJhbWUuIFdoYXQgb3RoZXIgbWV0aG9kcyBjb3VsZCB3ZSB1c2UgdG8gYWNjb21wbGlzaCB0aGUgc2FtZSBmZWF0PyBVc2UgdGhlIGJlbG93IGNvZGUgY2VsbCB0byBoZWxwIHlvdXJzZWxmIG91dC4NCjo6Og0KDQpgYGB7cn0NCiMgUmVsb2NhdGUgb3VyIHRhcmdldCBjb2x1bW4gdXNpbmcgdGhlIHNlbGVjdCgpIGNvbW1hbmQNCmNvdmlkX3BodV9sb25nX2NvcHkuZGYgICU+JSANCnJlbmFtZSh0b3RhbF9waHVfbmV3ID0gVG90YWwsDQogICAgICAgZGF0ZSA9IERhdGUpICU+JSANCiMgcmVsb2NhdGUgb3VyIHRvdGFsIGNvbHVtbiB0byB0aGUgcmlnaHQgc2lkZQ0KLi4uICU+JSANCg0KaGVhZCgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDMuMy4wIFNhdmUgeW91ciBkYXRhIHRvIGEgZmlsZSAtICJDb3VudHJ5IHJvYWRzLi4uIHNhdmUgdG8gaG9tZSEiDQoNCkF0IHRoaXMgcG9pbnQgd2UgaGF2ZSBjb21wbGV0ZWQgdGhlIGRhdGEgd3JhbmdsaW5nIHdlIHdhbnQgdG8gYWNjb21wbGlzaCBvbiB0aGlzIGRhdGFzZXQuIFdlJ3ZlIGNvbnZlcnRlZCBpdCB0byBhIGxvbmctZm9ybWF0IGFuZCByZW5hbWVkIHRoZSBQSFUgZW50cmllcyB3aGlsZSByZW1vdmluZyBhbnkgYE5BYCB2YWx1ZXMgdGhhdCBtYXkgY2F1c2UgaXNzdWVzLiBUaGVyZSBhcmUgYSBudW1iZXIgb2Ygd2F5cyB3ZSBjb3VsZCBzYXZlIHRoaXMgZGF0YSBub3cgZWl0aGVyIGFzIGEgdGV4dCBmaWxlIG9yIGluIGl0cyBjdXJyZW50IGZvcm0gYXMgYSBkYXRhIGZyYW1lIGluIGEgKiouUkRhdGEqKiBmb3JtYXQuDQoNCi0gICBXcml0ZSBvdXQgdG8gYSBkZWxpbWl0ZWQgZmlsZTogYHdyaXRlX2RlbGltKClgLCBgd3JpdGVfY3N2KClgLCBgd3JpdGVfdHN2KClgLCBgd3JpdGVfZXhjZWxfY3N2KClgDQotICAgV3JpdGUgb3V0IHRvIGEgZmlsZSwgbGluZSBieSBsaW5lOiBgd3JpdGVfbGluZXMoKWANCi0gICBTYXZlIGFuIG9iamVjdCB0byBhIC5SZGF0YSBmaWxlOiBgc2F2ZSgpYA0KLSAgIExvYWQgYW4gb2JqZWN0IGZyb20gYSAuUmRhdGEgZmlsZTogYGxvYWQoKWANCg0KTGV0J3MgdHJ5IHNvbWUgb2YgdGhvc2UgbWV0aG9kcyBub3cuDQoNCmBgYHtyfQ0KIyBDaGVjayB0aGUgZmlsZXMgbmFtZXMgd2UgY3VycmVudGx5IGhhdmUNCnByaW50KGRpcigiLi9kYXRhLyIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBXcml0ZSBjb3ZpZF9waHVfbG9uZy5kZiB0byBhIHRhYi1kZWxpbWl0ZWQgZmlsZQ0KLi4uKGNvdmlkX3BodV9sb25nLmRmLCBmaWxlID0gIi4vZGF0YS9PbnRhcmlvX2RhaWx5X2NoYW5nZV9pbl9jYXNlc19ieV9waHVfbG9uZy50c3YiKQ0KDQojIENoZWNrIG91ciBmaWxlIG5hbWVzIGFmdGVyIHdyaXRpbmcNCnByaW50KGRpcigiLi9kYXRhLyIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTYXZlIG91ciBkYXRhIGZyYW1lIGFzIGFuIG9iamVjdA0Kc2F2ZShjb3ZpZF9waHVfbG9uZy5kZiwgZmlsZT0iLi9kYXRhL09udGFyaW9fZGFpbHlfY2hhbmdlX2luX2Nhc2VzX2J5X3BodV9sb25nLlJEYXRhIikNCg0KIyBDaGVjayBvdXIgZmlsZSBuYW1lcyBhZnRlciBzYXZpbmcNCnByaW50KGRpcigiLi9kYXRhLyIpKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4zLjAuMSBgcmVhZHhsYCBhbmQgYHdyaXRleGxgIHBhY2thZ2VzIGZvciB3b3JraW5nIHdpdGggZXhjZWwgc3ByZWFkc2hlZXRzDQoNCk5vdCBhbGwgb2YgeW91ciBkYXRhIG1heSBjb21lIGFzIGEgY29tbWEtIG9yIHRhYi1kZWxpbWl0ZWQgZm9ybWF0LiBJbiB0aGUgY2FzZSBvZiBleGNlbCBzcHJlYWRzaGVldHMgdGhlcmUgYXJlIHNvbWUgcGFja2FnZXMgYXZhaWxhYmxlIHRoYXQgY2FuIGFsc28gZmFjaWxpdGF0ZSB0aGUgcGFyc2luZyBvZiB0aGVzZSBtb3JlIGNvbXBsZXggZmlsZXMuIFRoZSBgcmVhZHhsYCBwYWNrYWdlIGlzIHBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgIGJ1dCBgd3JpdGV4bGAgcGFja2FnZSBpcyBub3QuIFRoZXJlIGFyZSBvdGhlciBtZWFucyBvZiB3cml0aW5nIHRvIGFuIGV4Y2VsIGZpbGUgZm9ybWF0IGJ1dCB0aGV5IGFyZSBkZXBlbmRlbnQgb24gb3RoZXIgcHJvZ3JhbXMgKGxpa2UgSmF2YSBvciBFeGNlbCkgb3IgdGhlaXIgZHJpdmVycy4NCg0KRnJvbSB0aGUgYHJlYWR4bGAgcGFja2FnZQ0KDQotICAgR2V0IGEgbGlzdCBvZiBzaGVldCBuYW1lcyBmcm9tIGEgZmlsZTogYGV4Y2VsX3NoZWV0cygpYA0KLSAgIFJlYWQgaW4gYW4gZXhjZWwgc2hlZXQ6IGByZWFkX2V4Y2VsKClgDQoNCkZyb20gdGhlIGB3cml0ZXhsYCBwYWNrYWdlIChub3QgYSBwYXJ0IG9mIHRoZSB0aWR5dmVyc2UpIGJ1dCBpbmRlcGVuZGVudCBvZiBKYXZhIGFuZCBFeGNlbA0KDQotICAgV3JpdGUgb3V0IHRvIHhsc3ggZm9ybWF0OiBgd3JpdGVfeGxzeCgpYA0KLSAgIENhbiB3cml0ZSBhIGxpc3Qgb2Ygb2JqZWN0cyB0byBzZXBhcmF0ZSBzaGVldHMgYnV0IGNhbm5vdCBhcHBlbmQgdG8gcHJlLWV4aXN0aW5nIGZpbGVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA0LjAuMCBTaW1wbGUgZ3JhcGhpY2FsIGFuYWx5c2lzIG9mIGRhdGEgd2l0aCBgZ2dwbG90MmANCg0KV2Ugbm93IGhhdmUgc29tZSBkYXRhIGluIGEgdGlkeSBmb3JtYXQgdGhhdCB3ZSdkIGxpa2UgdG8gdmlzdWFsaXplLiBXZSBjYW4gYmVnaW4gd2l0aCBzb21lIGluaXRpYWwgYW5hbHlzZXMgb2YgdGhlIGRhdGEgdXNpbmcgdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlLiBJdCBoYXMgYWxsIG9mIHRoZSBjb21wb25lbnRzIHdlIG5lZWQgdG8gaGVscCB1cyBkZWNpZGUgb24gd2hpY2ggZGF0YSB3ZSB3YW50IHRvIGZvY3VzIG9uIG9yIGtlZXAuIFRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRvIHZpc3VhbGl6ZSBvdXIgZGF0YSBhbmQgaGVyZSB3ZSB3aWxsIHJlZnJlc2ggb3VyIGBnZ3Bsb3RgIHNraWxscy4NCg0KQmFzaWMgZ2dwbG90IG5vdGVzOiAtIGBnZ3Bsb3RgIG9iamVjdHMgaG9sZCBhIGNvbXBsZXggbnVtYmVyIG9mIGF0dHJpYnV0ZXMgYnV0IGFsd2F5cyBuZWVkIGFuIGluaXRpYWwgc291cmNlIG9mIGRhdGENCg0KLSAgIGBnZ3Bsb3RgIG9iamVjdHMgY2FuIGJlIG1vZGlmaWVkIHdpdGggdGhlIGArYCBzeW1ib2wgYnkgYWRkaW5nIGluIGxheWVycw0KICAgIC0gICBsYXllcnMgY2FuIGFsdGVyIGF0dHJpYnV0ZXMgc3VjaCBhcyB3aGljaCBkYXRhIGlzIGRpc3BsYXllZCBhbmQgaG93Lg0KICAgIC0gICBNb3N0IGxheWVycyBjYW4gYmUgbW9kaWZpZWQgaW4gb25lIHdheSBvciBhbm90aGVyLg0KLSAgIGBnZ3Bsb3RgIG9iamVjdHMgY2FuIGJlIHBsb3R0ZWQsIHNhdmVkLCBhbmQgcGFzc2VkIGFyb3VuZC4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1pbmZvfQ0KQXMgd2Ugc3RhcnQgdG8gcHJvZHVjZSBwbG90IGZpZ3VyZXMsIHRoZXknbGwgdmFyeSBpbiBzaXplIGRlcGVuZGluZyBvbiB5b3VyIG5lZWRzLiBJbiBhbiBSIE1hcmtkb3duIGNvZGUgY2VsbCwgeW91IGNhbiBzZXQgeW91ciBmaWd1cmUgc2l6ZSB1c2luZyB0aGUgY29kZSBjZWxsIGF0dHJpYnV0ZXMgbXVjaCBsaWtlIHRoZSBwYXJhbWV0ZXJzIG9mIGEgZnVuY3Rpb24uIFlvdSBjYW4gc2V0IHRoZSBmaWd1cmUgc2l6ZSBkaW1lbnNpb25zIHVzaW5nIDxiPmZpZy53aWR0aDwvYj4gYW5kIDxiPmZpZy5oZWlnaHQ8L2I+LiBBcyB3ZSBwcm9jZWVkIGluIHRoZSBmdXR1cmUsIHlvdSdsbCBzZWUgdXMgc2V0dGluZyB0aGVzZSBhdHRyaWJ1dGVzIHdpdGhpbiBvdXIgY29kZSBjZWxscy4NCjo6Og0KDQpgYGB7cn0NCiMgSW5pdGlhbGl6ZSBhIHBsb3Qgd2l0aCBvdXIgZGF0YQ0KcGh1LnBsb3QgPC0gZ2dwbG90KC4uLikNCg0KIyBUYWtlIGEgcXVpY2sgbG9vayBhdCB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhDQpzdHIocGh1LnBsb3QpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDQuMS4wIE1ha2UgYSBsaW5lIGdyYXBoIG9mIG5ldyBjYXNlcyBiYXNlZCBvbiBlYWNoIFBIVSBhY3Jvc3MgYWxsIGRhdGVzDQoNCldlIG5vdyBoYXZlIGEgYmFzaWMgcGxvdCBvYmplY3QgaW5pdGlhbGl6ZWQgYnV0IHdlIG5lZWQgdG8gdGVsbCBpdCBob3cgdG8gZGlzcGxheSB0aGUgZGF0YSBhc3NvY2lhdGVkIHdpdGggaXQuIFdlJ2xsIGJlZ2luIHdpdGggYSBzaW1wbGUgbGluZSBncmFwaCBvZiBhbGwgdGhlIHB1YmxpYyBoZWFsdGggdW5pdHMgYWNyb3NzIGFsbCBkYXRlcyB3aXRoaW4gdGhlIHNldC4NCg0KSW4gb3JkZXIgdG8gdXBkYXRlIG9yIGFkZCBsYXllcnMgdG8gYSBgZ2dwbG90YCBvYmplY3QsIHdlIGNhbiB1c2UgdGhlIGArYCBzeW1ib2wgZm9yIGVhY2ggY29tbWFuZC4gRm9yIGluc3RhbmNlLCB0byBkZWZpbmUgdGhlIHNvdXJjZSBvZiB4LWF4aXMgYW5kIHktYXhpcyBkYXRhLCB3ZSB1c2UgYGFlcygpYCBjb21tYW5kIHRvIHVwZGF0ZSB0aGUgYWVzdGhldGljcyBsYXllci4gUmVtZW1iZXIgaG93IHdlIGRlZmluZWQgdGhlIGBwdWJsaWNfaGVhbHRoX3VuaXRgIHZhcmlhYmxlIGFzIGEgZmFjdG9yPyBXZSdsbCB0YWtlIGFkdmFudGFnZSBvZiB0aGF0IGhlcmUgYW5kIHRlbGwgYGdncGxvdGAgdG8gZ2l2ZSBlYWNoIFBIVSBpdCdzIG93biBjb2xvdXIuDQoNCkFmdGVyIGRlZmluaW5nIG91ciBhZXN0aGV0aWNzLCB3ZSBzdGlsbCBuZWVkIHRvIHRlbGwgYGdncGxvdGAgaG93IHRvIGFjdHVhbGx5IGdyYXBoIHRoZSBkYXRhLiBUaGUgYGdncGxvdGAgcGFja2FnZSBjb21lcyB3aXRoIGFuIGFidW5kYW5jZSBvZiB2aXN1YWxpemF0aW9ucyBhY2Nlc3NlZCB0aHJvdWdoIHRoZSBgZ2VvbV8qKClgIGNvbW1hbmRzLiBTb21lIGV4YW1wbGVzIGluY2x1ZGUNCg0KLSAgIGBnZW9tX3BvaW50KClgIGZvciBzY2F0dGVycGxvdHMNCi0gICBgZ2VvbV9saW5lKClgIGZvciBsaW5lIGdyYXBocw0KLSAgIGBnZW9tX2JveHBsb3QoKWAgZm9yIGJveHBsb3RzDQotICAgYGdlb21fdmlvbGluKClgIGZvciB2aW9saW4gcGxvdHMNCi0gICBgZ2VvbV9iYXIoKWAgZm9yIGJhcmdyYXBocw0KLSAgIGBnZW9tX2hpc3RvZ3JhbSgpYCBmb3IgaGlzdG9ncmFtcw0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTd9DQojIFVwZGF0ZSB0aGUgYWVzdGhldGljcyB3aXRoIGF4aXMgYW5kIGNvbG91ciBpbmZvcm1hdGlvbiwgdGhlbiBhZGQgYSBsaW5lIGdyYXBoIQ0KcGh1LnBsb3QgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gLi4uLCB5ID0gLi4uLCBjb2xvdXIgPSAuLi4pICsNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHVibGljIEhlYWx0aCBVbml0IikpICsgIyBMZWdlbmQgdGl0bGUNCiAgICB4bGFiKCJEYXRlIikgKyAjIFNldCB0aGUgeC1heGlzIGxhYmVsDQogICAgeWxhYigiTmV3IGNhc2VzIikgKyAjIFNldCB0aGUgeS1heGlzIGxhYmVsDQoNCiAgICAjIDQuIEdlb21zDQogICAgZ2VvbV9saW5lKCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC4yLjAgVXNlIHRoZSBgZmFjZXRfd3JhcCgpYCBjb21tYW5kIHRvIGJyZWFrIFBIVXMgaW50byBzZXBhcmF0ZSBncmFwaHMNCg0KVGhlcmUncyBhIGxvdCBvZiBkYXRhIG9uIHRoYXQgZ3JhcGggYW5kIHNvbWUgb2YgaXQgaXMgcXVpdGUgZHJvd25lZCBvdXQgYmVjYXVzZSBvZiB0aGUgc2NhbGUgb2YgUEhVcyB3aXRoIG1hbnkgbW9yZSBjYXNlcy4gVG8gYnJlYWsgb3V0IGVhY2ggUEhVIGluZGl2aWR1YWxseSwgd2UgY2FuIGFkZCB0aGUgYGZhY2V0X3dyYXAoKWAgY29tbWFuZC4gV2UnbGwgYWxzbyB1cGRhdGUgc29tZSBvZiB0aGUgcGFyYW1ldGVyczoNCg0KLSAgIGBzY2FsZWA6IHdlIHdpbGwgdXBkYXRlIHRoaXMgc28gZWFjaCB5LWF4aXMgc2NhbGUgaXMgZGV0ZXJtaW5lZCBieSBQSFUtc3BlY2lmaWMgZGF0YS4NCi0gICBgbmNvbGA6IHVzZSB0aGlzIHRvIHNldCB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgZGlzcGxheWVkIGluIG91ciBncmlkDQoNCkF0IHRoZSBzYW1lIHRpbWUsIHdlJ2xsIGFsc28gZ2V0IHJpZCBvZiB0aGUgbGVnZW5kIHNpbmNlIGVhY2ggaW5kaXZpZHVhbCBncmFwaCB3aWxsIGJlIGxhYmVsZWQgYnkgaXRzIFBIVS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMSwgZmlnLmhlaWdodD0zMH0NCiMgVGhpcyBpcyBnb2luZyB0byBiZSBhIGJpZyBncmFwaCBzbyBhZGp1c3Qgb3VyIHBsb3Qgd2luZG93IHNpemVzIGZvciB1cw0Kb3B0aW9ucyhyZXByLnBsb3Qud2lkdGg9MjAsIHJlcHIucGxvdC5oZWlnaHQ9MzApDQoNCiMgQWRkIGEgZmFjZXRfd3JhcCBhbmQgZ2V0IHJpZCBvZiB0aGUgbGVnZW5kDQpwaHVfZmFjZXQucGxvdCA8LSBwaHUucGxvdCArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSBkYXRlLCB5ID0gbmV3X2Nhc2VzLCBjb2xvdXIgPSBwdWJsaWNfaGVhbHRoX3VuaXQpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICAgICAgDQogICAgIyBHaXZlIHRpdGxlcyB0byB5b3VyIGF4ZXMNCiAgICB4bGFiKCJEYXRlIikgKyAjIFNldCB0aGUgeC1heGlzIGxhYmVsDQogICAgeWxhYigiTmV3IGNhc2VzIikgKyAjIFNldCB0aGUgeS1heGlzIGxhYmVsDQogICAgZ2d0aXRsZSgiTmV3IGNhc2VzIHBlciBkYXkgYWNyb3NzIE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0cyIpICsNCg0KICAgICMgUmVtb3ZlIHRoZSBsZWdlbmQNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUoKSArDQoNCiAgICAjIDcuICMjIyA0LjIuMCBGYWNldCBvdXIgZGF0YSBieSBQSFUNCiAgICBmYWNldF93cmFwKH4gLi4uLCBzY2FsZXMgPSAuLi4sIG5jb2w9Li4uKQ0KDQojIERpc3BsYXkgb3VyIHBsb3QNCnBodV9mYWNldC5wbG90DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDQuMy4wIFVzZSB0aGUgYGdnc2F2ZSgpYCBjb21tYW5kIHRvIHNhdmUgeW91ciBwbG90cyB0byBhIGZpbGUNCg0KVGhlcmUgYXJlIGEgbnVtYmVyIG9mIHdheXMgeW91IGNhbiB1c2UgdGhlIGBnZ3NhdmUoKWAgY29tbWFuZCB0byBzcGVjaWZ5IGhvdyB5b3Ugd2FudCB0byBzYXZlIHlvdXIgZmlsZXMuDQoNCmBgYHtyfQ0KIyBXaGF0IGlzIG91ciB3b3JraW5nIGRpcmVjdG9yeT8NCmdldHdkKCkNCg0KIyBTYXZlIHRoZSBwbG90IHdlJ3ZlIGdlbmVyYXRlZCB0byB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhlIGxlY3R1cmUgZmlsZXMuDQpnZ3NhdmUoLi4uLCANCiAgICAgICBmaWxlbmFtZSA9ICJkYXRhL09udGFyaW9fcGh1X2RhdGEuYWxsLmZhY2V0LnBuZyIsIA0KICAgICAgIHNjYWxlPTIsIA0KICAgICAgIGRldmljZSA9ICJwbmciLCANCiAgICAgICB1bml0cyA9IGMoImNtIiksIHdpZHRoID0gMjAsIGhlaWdodCA9IDMwKQ0KDQojIFRha2UgYSBsb29rIGF0IHRoZSBkaXJlY3RvcnkNCmRpcigiZGF0YS8iKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjQuMCBCYXJwbG90cyBjYW4gYmUgdXNlZCB0byBzdW1tYXJpemUgeW91ciBkYXRhIGFjcm9zcyBQSFVzDQoNCkFsdGhvdWdoIHdlIGRvIGhhdmUgYSBydW5uaW5nIHRvdGFsIGZvciBlYWNoIGRhdGUsIHdoYXQgaWYgd2Ugd2FudCB0byBsb29rIGF0IHRoZSB0b3RhbHMgY2FzZXMgYWNyb3NzIHN1YnNldHMgb2YgdGhlIFBIVXM/IFVzaW5nIGEgYmFycGxvdCB3ZSBjYW4gc3RhY2sgY2FzZXMgYnkgZGF0ZSBhbmQgZ2V0IGEgc2Vuc2Ugb2YgZGFpbHkgY2FzZSB0b3RhbHMgZnJvbSB3aGljaCBzZXRzIG9mIFBIVXMgd2UgZGVzaXJlLg0KDQpUaGlzIHRpbWUgd2Ugd2lsbCB1c2UgYGdlb21fYmFyKClgIHRvIGRpc3BsYXkgb3VyIGRhdGEgYW5kIHRlbGwgaXQgdG8gdXNlIHRoZSB2YWx1ZXMgZnJvbSBvdXIgYG5ld19jYXNlc2AgdmFyaWFibGUgdG8gZ2VuZXJhdGUgdGhlIHRvdGFscy4gV2UgZG8gdGhpcyBieSBzZXR0aW5nIHRoZSBgc3RhdCA9ICJpZGVudGl0eSJgIHBhcmFtZXRlci4NCg0KQXQgdGhlIHNhbWUgdGltZSwgbGV0J3MgdXBkYXRlIG91ciBjb2xvdXJzIHRvIHVzZSBhIGNvbG91ci1ibGluZCBmcmllbmRseSBwYWxldHRlIHNjaGVtZS4NCg0KYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMH0NCg0KcGh1LnBsb3QgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gZGF0ZSwgeT0gbmV3X2Nhc2VzLCBmaWxsID0gLi4uKSArICMgc2V0IG91ciBmaWxsIGNvbG91ciBpbnN0ZWFkIG9mIGxpbmUgY29sb3VyDQoNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpICsgIyBzZXQgdGV4dCBzaXplDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGU9IlB1YmxpYyBIZWFsdGggVW5pdCIpKSArDQoNCiAgICAjIEdpdmUgdGl0bGVzIHRvIHlvdXIgYXhlcw0KICAgIHhsYWIoIkRhdGUiKSArICMgU2V0IHRoZSB4LWF4aXMgbGFiZWwNCiAgICB5bGFiKCJOZXcgY2FzZXMiKSArICMgU2V0IHRoZSB5LWF4aXMgbGFiZWwNCiAgICBnZ3RpdGxlKCJOZXcgY2FzZXMgcGVyIGRheSBhY3Jvc3MgYWxsIE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0cyIpICsNCg0KICAgICMgU2V0IHVwIG91ciBiYXJwbG90IGhlcmUNCiAgICBnZW9tX2JhciguLi4pICsgDQogICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSAjIHRoZSAiZCIgc3RhbmRzIGZvciBkaXNjcmV0ZSBjb2xvdXIgc2NhbGUNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuNC4xIEFsdGVyIHlvdXIgYmluIHdpZHRocyB0byBtb250aGx5IHRvdGFscyBieSB0cmFuc2Zvcm1pbmcgeW91ciB4LWF4aXMNCg0KRnJvbSBhYm92ZSB3ZSBnZXQgYSBzZW5zZSBvZiBvdmVyYWxsIHRvdGFscyBmb3Igc29tZSBQSFUgZGlzdHJpYnV0aW9ucyBidXQgaXQncyBzdGlsbCB0b28gbXVjaCB0byBsb29rIGF0LiBMZXQncyB0cmFuc2Zvcm0gb3VyIHgtYXhpcyB2YWx1ZXMgc28gd2UgY2FuIGJpbiBieSBtb250aHMgaW5zdGVhZC4gVG8gYWNjb21wbGlzaCB0aGlzIHdlJ2xsIHVzZSB0aGUgYGFzLnllYXJtb24oKWAgZnVuY3Rpb24gZm91bmQgaW4gdGhlIGB6b29gIHBhY2thZ2Ugd2UgbG9hZGVkIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIGxlY3R1cmUuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCnBodS5wbG90ICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeCA9IC4uLiwgIyMjIDQuNC4xIFVwZGF0ZSB0aGUgeC1heGlzIGJ5IHRyYW5zZm9ybWluZyB0aGUgdmFsdWVzIHRvIHllYXItbW9udGggZm9ybWF0DQogICAgICAgIHk9IG5ld19jYXNlcywgDQogICAgICAgIGZpbGwgPSBwdWJsaWNfaGVhbHRoX3VuaXQpICsgIyBzZXQgb3VyIGZpbGwgY29sb3VyIGluc3RlYWQgb2YgbGluZSBjb2xvdXINCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHVibGljIEhlYWx0aCBVbml0IikpICsNCg0KICAgICMgR2l2ZSB0aXRsZXMgdG8geW91ciBheGVzDQogICAgeGxhYigiRGF0ZSIpICsgIyBTZXQgdGhlIHgtYXhpcyBsYWJlbA0KICAgIHlsYWIoIk5ldyBjYXNlcyIpICsgIyBTZXQgdGhlIHktYXhpcyBsYWJlbA0KICAgIGdndGl0bGUoIk5ldyBjYXNlcyBwZXIgbW9udGggYWNyb3NzIGFsbCBPbnRhcmlvIFB1YmxpYyBIZWFsdGggVW5pdHMiKSArDQoNCiAgICAjIFNldCB1cCBvdXIgYmFycGxvdCBoZXJlDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgDQogICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSAjIHRoZSAiZCIgc3RhbmRzIGZvciBkaXNjcmV0ZSBjb2xvdXIgc2NhbGUNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC41LjAgRmlsdGVyIHlvdXIgZGF0YSBmb3Igd2hhdCB5b3Ugd2FudCB0byBkaXNwbGF5DQoNCk5vdyB0aGF0IHdlIGhhdmUgdGFrZW4gYW4gaW5pdGlhbCBsb29rIGF0IG91ciBkYXRhLCB3ZSBjYW4gc2VlIHRoYXQgZXZlbiBhZnRlciBjb252ZXJ0aW5nIG91ciBheGlzIHRvIGEgbW9udGgteWVhciBmb3JtYXQsIGl0IGFwcGVhcnMgdGhhdCBzb21lIG9mIHRoZSBkYXRhIGlzbid0IHRoYXQgcmVsZXZhbnQgZm9yIHVzLiBTb21lIG9mIHRoZSBQSFVzIGFyZSBub3QgZ2VuZXJhdGluZyBtYW55IG5ldyBjYXNlcyBwZXIgZGF5IHNvIHdlIGNhbiBub3cgY29uc2lkZXIgc2xpY2luZyBvdXIgZGF0YSB1cCB0byBsb29rIGF0IHNwZWNpZmljIHJlZ2lvbnMuDQoNCkxldCdzIGxvb2sgYXQgdGhlIHRvcCAxMCByZWdpb25zIGJ5IHRvdGFsIGNhc2Vsb2FkIGFjcm9zcyB0aGUgZGF0YXNldC4NCg0KYGBge3J9DQojIFdoYXQgYXJlIHRoZSB0b3AgMTAgcmVnaW9ucyBieSB0b3RhbCBjYXNlbG9hZD8NCmNvdmlkX3BodV9sb25nLmRmICU+JSANCg0KIyBncm91cCB0aGUgZGF0YSBieSBwdWJsaWMgaGVhbHRoIHVuaXQNCmdyb3VwX2J5KC4uLikgJT4lIA0KDQojIFN1bW1hcml6ZSBpdCBieSB0aGUgdG90YWwgbnVtYmVyIG9mIG5ldyBjYXNlcyBpbiBlYWNoIFBIVQ0Kc3VtbWFyaXNlKC4uLikgJT4lIA0KDQojIFNvcnQgYWxsIG9mIHRoZSBkYXRhIGluIGRlc2NlbmRpbmcgb3JkZXIgYnkgdG90YWwgY2FzZXMNCmFycmFuZ2UoLi4uKSAlPiUgDQoNCiMgdGFrZSB0aGUgdG9wIDEwIFBIVXMNCi5bMToxMCwgXQ0KYGBgDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSBhIGxpc3Qgb2YgYWxsIFBIVXMgYW5kIHNvcnQgYnkgdG90YWwgY2FzZWxvYWQNCiMgR2VuZXJhdGUgYSBsaXN0IG9mIGFsbCBQSFVzIGFuZCBzb3J0IGJ5IHRvdGFsIGNhc2Vsb2FkDQpwaHVfYnlfdG90YWxfY2FzZXNfZGVzYyA8LSBjb3ZpZF9waHVfbG9uZy5kZiAlPiUgDQoNCiMgR3JvdXAgYnkgcHVibGljIGhlYWx0aCB1bml0DQpncm91cF9ieShwdWJsaWNfaGVhbHRoX3VuaXQpICU+JSANCg0KIyBCYXNlZCBvbiBwdWJsaWMgaGVhbHRoIHVuaXQsIHN1bSB0aGUgdG90YWwgY2FzZXMNCnN1bW1hcmlzZSh0b3RhbF9jYXNlcyA9IHN1bShuZXdfY2FzZXMpKSAlPiUgDQoNCiMgU29ydCBieSBkZXNjZW5kaW5nIG9yZGVyDQphcnJhbmdlKGRlc2ModG90YWxfY2FzZXMpKSAlPiUgDQoNCiMgR3JhYiB0aGUgUEhVIG5hbWVzIGFuZCBjb252ZXJ0IHRoZW0gaW50byBhIGNoYXJhY3RlciB2ZWN0b3INCnNlbGVjdCguLi4pICU+JSANCnVubGlzdCgpICU+JSANCmFzLmNoYXJhY3RlcigpICMgQ29lcmNpb24gdG8gYSB2ZWN0b3IgcmVtb3ZlcyB0aGUgbmFtZXMuIHVubmFtZSgpIHdvcmtzIGFzIHdlbGwuDQoNCiMgVGFrZSBhIGxvb2sgYXQgdGhlIHB1YmxpYyBoZWFsdGggdW5pdHMNCnByaW50KHBodV9ieV90b3RhbF9jYXNlc19kZXNjKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC41LjEgVXNlIHRoZSBgZmlsdGVyKClgIGNvbW1hbmQgdG8gbWFrZSBhIHN1YnNldCBvZiBvdXIgZGF0YQ0KDQpOb3cgdGhhdCB3ZSBoYXZlIGEgbGlzdCBvZiBQSFVzIG9yZGVyZWQgYnkgZGVzY2VuZGluZyB0b3RhbCBjYXNlcywgd2UgY2FuIHVzZSB0aGF0IHRvIGZpbHRlciBvdXIgYGNvdmlkX3BodV9sb25nLmRmYCBkYXRhZnJhbWUgYW5kIGdyYXBoIG9ubHkgdGhlIG1vcmUgaGVhdmlseSBpbmZlY3RlZCBQSFVzLiBXZSBjYW4gdGhlbiBwaXBlIHRoZSBmaWx0ZXJlZCBkYXRhIG92ZXIgdG8gbWFrZSBhIGBnZ3Bsb3QoKWAgb2JqZWN0LiBBdCB0aGUgc2FtZSB0aW1lIHdlJ2xsIGRvIGEgZmV3IG1vcmUgdGhpbmdzOg0KDQoxLiAgUmVvcmRlciBvdXIgZmFjdG9ycyBzbyB0aGF0IHRoZSBiYXJzIGFuZCBsZWdlbmQgZGlzcGxheSB0aGUgUEhVcyBpbiBhc2NlbmRpbmcgb3JkZXIgYnkgbmV3IGNhc2VzLg0KMi4gIEFsdGVyIHRoZSBwbG90IHRpdGxlIHRvIHJlZmxlY3QgdGhlIGRhdGEgd2UgYXJlIHVzaW5nLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KIyBNYWtlIGEgYmFyIGdyYXBoDQpjb3ZpZF9waHVfbG9uZy5kZiAlPiUgDQoNCiMjIyA0LjUuMSBGaWx0ZXIgb3VyIGRhdGEgYmFzZWQgb24gdGhlIFBIVXMgd2Ugd2FudCB0byBzZWUNCmZpbHRlciguLi4pICU+JSANCg0KIyBSZWRpcmVjdCBvdXIgbmV3IGRhdGEgZnJhbWUgdG8gZ2dwbG90DQpnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4ID0gYXMueWVhcm1vbihkYXRlKSwgDQogICAgICAgIHk9IG5ld19jYXNlcywgDQogICAgICAgIGZpbGwgPSBmY3RfcmVvcmRlcihwdWJsaWNfaGVhbHRoX3VuaXQsIG5ld19jYXNlcykpICsgIyByZW9yZGVyaW5nIHRoZSBsZXZlbHMgb2YgdGhlIGRhdGEgc3VwcGxpZWQNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iUHVibGljIEhlYWx0aCBVbml0IikpICsNCg0KICAgICMgR2l2ZSB0aXRsZXMgdG8geW91ciBheGVzDQogICAgeGxhYigiRGF0ZSIpICsgIyBTZXQgdGhlIHgtYXhpcyBsYWJlbA0KICAgIHlsYWIoIk5ldyBjYXNlcyIpICsgIyBTZXQgdGhlIHktYXhpcyBsYWJlbA0KICAgIGdndGl0bGUoIk5ldyBjYXNlcyBwZXIgbW9udGggYWNyb3NzIHRvcCAzIE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0cyIpICsNCg0KICAgICMgU2V0IHVwIG91ciBiYXJwbG90IGhlcmUNCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyANCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICMgdGhlICJkIiBzdGFuZHMgZm9yIGRpc2NyZXRlIGNvbG91ciBzY2FsZQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjYuMCBMb29raW5nIGF0IHRoZSBlZmZlY3Qgb2YgbG9ja2Rvd24gb24gbmV3IGNhc2VzDQoNCldlIGNhbiBzZWUgZnJvbSBvdXIgZmlyc3QgZ3JhcGggb2YgZGFpbHkgY2FzZSBsb2FkcyB0aGF0IHRoZXJlIGNhbiBiZSBxdWl0ZSBhIGJpdCBvZiB2YXJpYWJpbGl0eSBmcm9tIGRheSB0byBkYXkuIFJhdGhlciB0aGFuIGxvb2sgYXQgdGhlIGRhaWx5IHRhbGx5IG9mIG5ldyBjYXNlcywgcGVyaGFwcyB3ZSBjYW4gdGFrZSBpbnRvIGFjY291bnQgdGhlIG92ZXJhbGwgbnVtYmVyIG9mIG5ldyBjYXNlcyBhcHBlYXJpbmcgaW4gYSAxNC1kYXkgc2xpZGluZyB3aW5kb3cuIEdpdmVuIHRoYXQgc3ltcHRvbXMgZnJvbSB0aW1lIG9mIGluZmVjdGlvbiBjYW4gdGFrZSBiZXR3ZWVuIDUtMTQgZGF5cyB0byBtYW5pZmVzdCwgdGhlbiBhIHBvcnRpb24gb2YgZGFpbHkgcG9zaXRpdmUgY2FzZXMgY2FuIGJlIHRoZSByZXN1bHQgb2YgaW5mZWN0aW9uIGdvaW5nIGJhY2sgYXMgZmFyIGFzIDE0LWRheXMuIFRha2luZyBhIGxvb2sgYXQgYSAxNC1kYXkgbWVhbiBhdmVyYWdlIHdpbGwgYWxzbyBzbW9vdGggb3V0IG91ciBkYXRhIGFzIHdlIHNlZSBiZWxvdzoNCg0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0Fzc2lnbm1lbnRzL0ExLTMuMy5jYXNlV2luZG93UGxvdC5wbmc/cmF3PXRydWUiIHdpZHRoPSIxMDAwIi8+DQoNClRvIGFjY29tcGxpc2ggdGhlIGFib3ZlIHZpc3VhbGl6YXRpb24sIHdlJ2xsIG5lZWQgdG8gcGVyZm9ybSBzb21lIHRyYW5zZm9ybWF0aW9ucyBvbiBvdXIgZGF0YXNldC4NCg0KMS4gIEVuc3VyZSBvdXIgZGF0YSBpcyBncm91cGVkIGJ5IHB1YmxpYyBoZWFsdGggdW5pdA0KMi4gIFN1bW1hcmlzZSBvdXIgZGF0YSBpbiBzbGlkaW5nIHdpbmRvd3Mgb2YgMTQtZGF5IGxlbmd0aA0KDQpXZSdsbCB3YW50IHRvIHRyYWNrIG9ic2VydmF0aW9ucyBieTogLSBwdWJsaWMgaGVhbHRoIHVuaXQgLSBjYXNlcyBpbiB0aGUgd2luZG93IC0gd2luZG93IHN0YXJ0IGRhdGUgLSB3aW5kb3cgZW5kIGRhdGUNCg0KYGBge3J9DQojIFNodXQgZG93biBzb21lIG91dHB1dCBpbmZvcm1hdGlvbiBmcm9tIHRoZSBzdW1tYXJpc2UgZnVuY3Rpb24NCm9wdGlvbnMoZHBseXIuc3VtbWFyaXNlLmluZm9ybSA9IEZBTFNFKQ0KDQojIDEuIGdyb3VwIG91ciBkYXRhIGJ5IHB1YmxpYyBoZWFsdGggdW5pdA0KY292aWRfcGh1X2xvbmcuZGYgPC0gY292aWRfcGh1X2xvbmcuZGYgJT4lIGdyb3VwX2J5KHB1YmxpY19oZWFsdGhfdW5pdCkNCg0KIyAyLiBnZXQgYSBjb21wbGV0ZSBsaXN0IG9mIGNhc2UgZGF0ZXMNCmNhc2UuZGF0ZXMgPC0gdW5pcXVlKGNvdmlkX3BodV9sb25nLmRmJGRhdGUpDQoNCiMgMy4gc2V0IHVwIGEgdGFibGUgdG8gaG9sZCBvdXIgc3VtbWFyaXNlZCByZXN1bHRzDQpwaHVfd2luZG93X2RhdGEuZGYgPSBkYXRhLmZyYW1lKHB1YmxpY19oZWFsdGhfdW5pdCA9IGNoYXJhY3RlcigwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aW5kb3dfbWVhbiA9IG51bWVyaWMoMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRfZGF0ZSA9IG51bWVyaWMoMCksIGVuZF9kYXRlID0gbnVtZXJpYygwKSkNCg0KY2FzZV93aW5kb3cgPSAxNC0xDQoNCiMgSXRlcmF0ZSB0aHJvdWdoIHRoZSBkYXRlcyBpbiBhIDE0LWRheSBzbGlkaW5nIHdpbmRvdw0KZm9yIChpIGluIDE6KGxlbmd0aChjYXNlLmRhdGVzKS1jYXNlX3dpbmRvdykpIHsNCiAgICANCiAgICBjdXJyLnNldCA8LSBjb3ZpZF9waHVfbG9uZy5kZiAlPiUNCiAgICAgICAgICAgICAgICAjIEZpbHRlciBmb3IgYSBzZXQgb2YgZGF0YSB0aGF0IHNwYW5zIDE0IGRheXMNCiAgICAgICAgICAgICAgICBmaWx0ZXIoZGF0ZSAlaW4lIGNhc2UuZGF0ZXNbaTooaSArIGNhc2Vfd2luZG93KV0pICU+JSANCiAgICAgICAgICAgICAgICAjIFN1bW1hcml6ZSB0aGF0IGRhdGEgYmFzZWQgb24gcHVibGljIGhlYWx0aCB1bml0DQogICAgICAgICAgICAgICAgc3VtbWFyaXplKHdpbmRvd19tZWFuID0gbWVhbihuZXdfY2FzZXMpKQ0KICAgIA0KICAgICMgVHJhY2sgdGhlIHN0YXJ0IGFuZCBlbmQgZGF0ZXMgb2YgdGhlIHdpbmRvdw0KICAgIGN1cnIuc2V0JHN0YXJ0X2RhdGUgPSBjYXNlLmRhdGVzWy4uLl0NCiAgICBjdXJyLnNldCRlbmRfZGF0ZSA9IGNhc2UuZGF0ZXNbLi4uXQ0KICAgIA0KICAgICMgQWRkIHRoaXMgdGFibGUgdG8gdGhlIGNvbGxlY3RlZCBkYXRhDQogICAgcGh1X3dpbmRvd19kYXRhLmRmIDwtIC4uLg0KfQ0KDQojIENoZWNrIG9uIHRoZSBmaW5hbCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGENCnN0cihwaHVfd2luZG93X2RhdGEuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjYuMSBQbG90IG91ciB3aW5kb3dlZCBkYXRhIGFzIGEgbGluZSBncmFwaA0KDQpOb3cgdGhhdCB3ZSd2ZSBnZW5lcmF0ZWQgb3VyIHdpbmRvd2VkIGRhdGEsIGxldCdzIHBsb3QgdGhlIHRvcCA1IFBIVXMgYnkgY2FzZWxvYWQuIExldCdzIGFsc28gYW5ub3RhdGUgc29tZSBkYXRlcyBmcm9tIHRoZSBvdXIgcGFuZGVtaWMgaGlzdG9yeToNCg0KLSAgIE1hcmNoIDIzcmQsIDIwMjA6IFRvcm9udG8gZGVjbGFyZXMgc3RhdGUgb2YgZW1lcmdlbmN5DQotICAgTWFyY2ggMjR0aCwgMjAyMDogT3VyIFBIVSBkYXRhIGNvbGxlY3Rpb25zIGJlZ2lucw0KLSAgIE1hcmNoIDMxc3QsIDIwMjA6IFRvcm9udG8gY2FuY2VscyBhbGwgY2l0eS1sZWQgbWFqb3IgZXZlbnRzDQotICAgTWF5IDIzcmQsIDIwMjA6IFRyaW5pdHkgQmVsbHdvb2RzIFBhcmsgaW5jaWRlbnQNCi0gICBKdWx5IDMxc3QsIDIwMjA6IFRvcm9udG8gaXMgYWxsb3dlZCB0byBlbnRlciBTdGFnZSAzIHJlb3BlbmluZw0KLSAgIFNlcHRlbWJlciAxNXRoLCAyMDIwOiBSZXR1cm4gdG8gcHVibGljIHNjaG9vbCBmb3IgVERTQg0KLSAgIE9jdG9iZXIgMTB0aCwgMjAyMDogNC13ZWVrIHJvbGxiYWNrIHRvIG1vZGlmaWVkIHN0YWdlIDINCi0gICBOb3ZlbWJlciAxNHRoLCAyMDIwOiBUb3JvbnRvIGZ1cnRoZXIgcmVzdHJpY3RlZCB0byBDb250cm9sL3JlZCB0aWVyLg0KLSAgIE5vdmVtYmVyIDIzcmQsIDIwMjA6IEFsbCBub24tZXNzZW50aWFsIHNlcnZpY2VzIGFyZSBvcmRlcmVkIGNsb3NlZA0KLSAgICoqRGVjZW1iZXIgMjZ0aCwgMjAyMDogU2Vjb25kIHByb3ZpbmNlLXdpZGUgc2h1dGRvd24gaXNzdWVkKioNCi0gICBGZWJydWFyeSAxNnRoLCAyMDIxOiBTdHVkZW50cyByZXR1cm4gdG8gaW4tcGVyc29uIGNsYXNzZXMNCi0gICBNYXJjaCA1dGgsIDIwMjE6IFRvcm9udG8gZXhpdHMgc3RheS1hdC1ob21lIG9yZGVycyBhbmQgZW50ZXJzIGxvY2tkb3duL2dyZXkgem9uZS4NCi0gICAqKkFwcmlsIDNyZCwgMjAyMTogVGhpcmQgcHJvdmluY2Utd2lkZSBzaHV0ZG93biBpc3N1ZWQqKg0KLSAgICoqTm92ZW1iZXIgMjh0aCwgMjAyMTogRmlyc3QgdHdvIGNhc2VzIG9mIE9taWNyb24gdmFyaWFudCByZXBvcnRlZCBpbiBPbnRhcmlvKioNCi0gICAqKkRlY2VtYmVyIDMxc3QsIDIwMjE6IE9udGFyaW8gbGltaXRzIFBDUiB0ZXN0aW5nKioNCg0KIyMjIDQuNi4yIEhlcmUncyB3aGF0IHdlJ2xsIGRvOg0KDQoxLiAgUGxvdCB0aGUgd2luZG93ZWQgZGF0YSBmaWx0ZXJlZCBieSB0aGUgdG9wIDUgUEhVcw0KMi4gIENsZWFuIHVwIHRoZSBncmFwaCBhIGxpdHRsZSBiaXQgYnkgInNpbXBsaWZ5aW5nIiB0aGUgdGhlbWVzDQozLiAgQW5ub3RhdGUgNCBkYXRlcyBmcm9tIHRoZSBwYW5kZW1pYyB0aW1lbGluZQ0KDQojIyMgNC42LjMgQWRkaXRpb25hbCBsYXllcnMgd2UnbGwgc2VlOg0KDQoxLiAgYHRoZW1lKClgOiB3ZSBjYW4gdXNlIHRoaXMgbGF5ZXIgdG8gYWNjZXNzIGFueSBudW1iZXIgb2YgZWxlbWVudHMgcmVnYXJkaW5nIHRoZSBvdmVyYWxsIGxvb2svZmVlbCBvZiBvdXIgdmlzdWFsaXphdGlvbg0KMi4gIGBzY2FsZV8qYDogdGhlIHNjYWxlIGxheWVycyBhbGxvdyB1cyB0byBhbHRlciB0aGUgcGFyYW1ldGVycyBvZiBob3cgb3VyIGF4aXMgdmFsdWVzIGFyZSBjYWxjdWxhdGVkIG9yIGV2ZW4gY29sb3VycyBvZiB2YXJpb3VzIGNvbXBvbmVudHMhDQozLiAgYGdlb21fdGV4dCgpYDogdXNlZCB0byBkaXJlY3RseSBhZGQgdGV4dCBiYXNlZCBvbiBhIG1peCBvZiB2YXJpYWJsZXMgcHVsbGVkIGZyb20geW91ciBkYXRhIG9yIHNwZWNpZmljIHN0YXJ0L2VuZCBwb2ludHMNCjQuICBgYW5ub3RhdGUoKWA6IGEgbGF5ZXIgdG8gb3ZlcmxheSBjb21wb25lbnRzIG9udG8geW91ciB2aXN1YWxpemF0aW9uIGxpa2Ugc2hhcGVzLCBvciBhcnJvd3MgZXRjLg0KDQpJbiB0aGUgY29taW5nIHdlZWtzIHdlJ2xsIGJlIGRpZ2dpbmcgaW50byB0aGUgbWVhbmluZyBvZiB0aGVzZSBtb3JlIGJ1dCBmb3IgdGhpcyB3ZWVrLCBpdCdzIGEgYml0IG9mIGEgdHJpYWwgYnkgZmlyZS9tZW1vcnkuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQojIEJ1aWxkIG91ciBwbG90IGFuZCBzYXZlIHRvIGFuIG9iamVjdA0KcGh1X3dpbmRvdy5wbG90IDwtIHBodV93aW5kb3dfZGF0YS5kZiAlPiUgDQojIEZpbHRlciBmb3IgdGhlIHRvcCA1IGluZmVjdGVkIFBIVXMNCmZpbHRlcihwdWJsaWNfaGVhbHRoX3VuaXQgJWluJSBwaHVfYnlfdG90YWxfY2FzZXNfZGVzY1sxOjVdKSAlPiUgDQoNCiMgcmVkaXJlY3QgdGhlIGZpbHRlcmVkIHJlc3VsdCB0byBnZ3Bsb3QNCmdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgYWVzKHggPSAuLi4sIHkgPSAuLi4sIGNvbG91ciA9IGZjdF9yZW9yZGVyKHB1YmxpY19oZWFsdGhfdW5pdCwgLi4uLCAuZGVzYz1UUlVFKSkgKw0KDQogICAgdGhlbWVfYncoKSArICMgU2ltcGxpZnkgdGhlIHRoZW1lDQogICAgeGxhYigiRGF0ZSIpICsNCiAgICB5bGFiKCJNZWFuIGNhc2VzIGluIDE0LWRheSB3aW5kb3ciKSArDQogICAgZ2d0aXRsZSgiTWVhbiBjYXNlcyBpbiBhIDE0LWRheSB3aW5kb3cgYWNyb3NzIHRvcCA1IE9udGFyaW8gUHVibGljIEhlYWx0aCBVbml0cyIpICsNCg0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAjIHNldCB0ZXh0IHNpemUNCiAgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlPSJQdWJsaWMgSGVhbHRoIFVuaXQiKSkgKyAjIHNldCBvdXIgbGVnZW5kIG5hbWUNCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXk5NSIpKSArICMgZGFya2VuIG91ciBtYWpvciB5IGdyaWQNCiAgICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsgIyByZW1vdmUgb3VyIG1pbm9yIHkgZ3JpZA0KICAgIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHJlbW92ZSBvdXIgbWlub3IgeCBncmlkDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSkpICsgIyByb3RhdGUgb3VyIHgtYXhpcyB0ZXh0DQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICAjIFN0YXJ0IGxvb2tpbmcgYXQgZGF0YSBmcm9tIE1hcmNoIDIwMjAgb253YXJkcw0KICAgIHNjYWxlX3hfZGF0ZShsaW1pdHMgPSBjKC4uLiksDQogICAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gLi4uLCBkYXRlX2xhYmVscyA9IC4uLikgKw0KDQogICAgc2NhbGVfY29sb3JfdmlyaWRpc19kKCkgKyANCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUobGluZXdpZHRoPTEuNSkgKyAjIE5vdGUgdGhhdCAic2l6ZT0xLjUiIHdvcmtzIGhlcmUgdG9vIGJ1dCBpcyBkZXByZWNhdGVkDQoNCiAgICAjIFdpbnRlciAyMDIwIGxvY2tkb3duDQogICAgZ2VvbV90ZXh0KGFlcyh4PWFzLkRhdGUoIjIwMjAtMTItMjYiKSArIDcsIGxhYmVsID0gIlByb3ZpbmNlLXdpZGUgbG9ja2Rvd24iLCB5PTIyMDApLCANCiAgICAgICAgICAgICAgYW5nbGU9OTAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInJlY3QiLCB4bWluPWFzLkRhdGUoIjIwMjAtMTItMjYiKSwgeG1heD1hcy5EYXRlKCIyMDIwLTEyLTI2IikgKyAxNCwgDQogICAgICAgICAgICAgeW1pbj0tSW5mLCB5bWF4PUluZiwgZmlsbD0icmVkIiwgYWxwaGE9MC4yKSArDQoNCiAgICAjIFNwcmluZyAyMDIxIExvY2tkb3duDQogICAgZ2VvbV90ZXh0KGFlcyh4PWFzLkRhdGUoIjIwMjEtMDQtMDMiKSArIDcsIGxhYmVsID0gIlByb3ZpbmNlLXdpZGUgbG9ja2Rvd24iLCB5PTIyMDApLCANCiAgICAgICAgICAgICAgYW5nbGU9OTAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInJlY3QiLCB4bWluPWFzLkRhdGUoIjIwMjEtMDQtMDMiKSwgeG1heD1hcy5EYXRlKCIyMDIxLTA0LTAzIikgKyAxNCwgDQogICAgICAgICAgICAgeW1pbj0tSW5mLCB5bWF4PUluZiwgZmlsbD0icmVkIiwgYWxwaGE9MC4yKSArDQoNCiAgICAjIE9taWNyb24gYXJyaXZlcw0KICAgIGdlb21fdGV4dChhZXMoeD1hcy5EYXRlKCIyMDIxLTExLTMwIiksIGxhYmVsID0gIkZpcnN0IE9taWNyb25cbmNhc2VzIHJlcG9ydGVkXG5pbiBPbnRhcmlvIiwgeT0xMDAwKSwgDQogICAgICAgICAgICAgIGhqdXN0PTEsIHZqdXN0ID0gMCwgc2l6ZT0xMCwgY29sb3VyPSJibGFjayIpICsNCiAgICBhbm5vdGF0ZSgic2VnbWVudCIsIHg9YXMuRGF0ZSgiMjAyMS0xMS0yOCIpLCB4ZW5kID0gYXMuRGF0ZSgiMjAyMS0xMS0yOCIpLA0KICAgICAgICAgICAgIHk9ODAwLCB5ZW5kPTEwMCwgY29sb3VyPSJyZWQiLCBsaW5ld2lkdGggPSAyLCBhcnJvdyA9IGFycm93KCkpICsNCg0KICAgICMgT250YXJpbyBlbmRzIHByb3BlciBQQ1IgdGVzdGluZw0KICAgIGdlb21fdGV4dChhZXMoeD1hcy5EYXRlKCIyMDIyLTAzLTAxIiksIGxhYmVsID0gIk9udGFyaW8gcmVkdWNlcyBwdWJsaWNcblBDUiBDT1ZJRC0xOSB0ZXN0aW5nIiwgeT0yNTAwKSwgDQogICAgICAgICAgICAgIGhqdXN0PTAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInNlZ21lbnQiLCB4PWFzLkRhdGUoIjIwMjItMDMtMDEiKSwgeGVuZCA9IGFzLkRhdGUoIjIwMjEtMTItMzEiKSwNCiAgICAgICAgICAgICB5PTI1MDAsIHllbmQ9MjUwMCwgY29sb3VyPSJyZWQiLCBsaW5ld2lkdGggPSAyLCBhcnJvdyA9IGFycm93KCkpIA0KDQojIHBsb3Qgb3VyIG9iamVjdCB0byBzdGFuZGFyZCBvdXRwdXQNCnBodV93aW5kb3cucGxvdA0KDQojIElmIHlvdSB3YW50ZWQgdG8gc2F2ZSB5b3VyIHBsb3Q6DQojIGdnc2F2ZShwaHVfd2luZG93LnBsb3QsIGZpbGU9ImltYWdlcy90b3A1X1BIVV9jYXNlc18xNGQtd2luZG93LnBuZyIsIHNjYWxlPTEsIGRldmljZSA9ICJwbmciLCB1bml0cyA9IGMoImluIiksIHdpZHRoPTIwLCBoZWlnaHQ9MTApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDQuNy4wIERvbid0IGZvcmdldCB0aGUgaG9zcGl0YWxpemF0aW9uIGRhdGEhDQoNCk9uZSBvZiB0aGUgbGFzdCB0aGluZ3Mgd2Ugd2FudCB0byBjb3ZlciBiZWZvcmUgd3JhcHBpbmcgdXAgaXMgdGhlIGltcG9ydGFuY2Ugb2YgZ3JvdXBpbmcgeW91ciBkYXRhIGFuZCBzdW1tYXJpemluZyBpdC4gVGhpcyBwYXJhZGlnbSBpcyBvZnRlbiBhIHNpbXBsZSBhbmQgcG93ZXJmdWwgd2F5IHRvIGdlbmVyYXRlIHN1bW1hcnkgaW5mb3JtYXRpb24gYWJvdXQgeW91ciB2YXJpb3VzIGRhdGEgZ3JvdXBzL2V4cGVyaW1lbnRzLg0KDQpMb29raW5nIGJhY2sgYXQgb3VyIGxhc3QgZGF0YSBzZXJpZXMsIGl0IHdhcyBub3RlZCB0aGF0IGFmdGVyIERlY2VtYmVyIDIwMjIsIHRoZSBtZXRyaWNzIGNvbmNlcm5pbmcgbmV3IGNhc2UgY291bnRzIGJlY2FtZSB1bnJlbGlhYmxlIGR1ZSB0byBhIHJlZHVjdGlvbiBpbiBDT1ZJRC0xOSB0ZXN0aW5nIG9mIHRoZSBwdWJsaWMuIEluc3RlYWQsIGR1ZSB0byB0aGUgaW5mbHV4IG9mIGNhc2VzLCBpdCBiZWNhbWUgbW9yZSBhY2N1cmF0ZSB0byBtb25pdG9yIG1ldHJpY3MgbGlrZSBob3NwaXRhbGl6YXRpb25zIGFuZCBDT1ZJRCB3YXN0ZSB3YXRlciBzaWduYWwuDQoNClRvIHRoaXMgZW5kLCBsZXQncyBsb29rIGF0IHRoZSBDT1ZJRCBob3NwaXRhbGl6YXRpb24gZGF0YSBieSBpbXBvcnRpbmcgYHJlZ2lvbl9ob3NwaXRhbF9pY19jb3ZpZF9kYXRhLmNzdmAgZnJvbSBvdXIgZGF0YSBmb2xkZXIuIFRoaXMgZ2l2ZXMgdXMgYW4gaWRlYSBvZiB0aGUgc3RyZXNzIGJlaW5nIGFwcGxpZWQgdG8gdGhlIGhlYWx0aGNhcmUgc3lzdGVtIGFuZCBjYW4gYWxzbyBnaXZlIHVzIGFuIGlkZWEgb2YgaG93IHNldmVyZSB0aGUgcGFuZGVtaWMgbWF5IGJlIGZyb20gd2F2ZSB0byB3YXZlLg0KDQpgYGB7cn0NCiMgSW1wb3J0IHRoZSBob3NwaXRhbGl6YXRpb24gZGF0YQ0KY292aWRfaG9zcGl0YWxpemF0aW9ucyA8LSByZWFkX2NzdiguLi4pDQoNCiMgVGFrZSBhIHF1aWNrIGxvb2sNCnN0cihjb3ZpZF9ob3NwaXRhbGl6YXRpb25zKQ0KDQojIEhvdyBtYW55IHJlZ2lvbnMgYXJlIHRoZXJlPw0KdW5pcXVlKGNvdmlkX2hvc3BpdGFsaXphdGlvbnMkb2hfcmVnaW9uKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC43LjEgVXNlIHRoZSBgZ3JvdXBfYnkoKWAgYW5kIGBzdW1tYXJpemUoKWAgcGFyYWRpZ20gdG8gYW5hbHlzZSBkYXRhDQoNClNvIGl0IGxvb2tzIGxpa2Ugb3VyIGhvc3BpdGFsaXphdGlvbiBkYXRhIGJlZ2lucyBhcm91bmQgQXByaWwgMjAyMCBhbmQgaW5jbHVkZXMgbXVsdGlwbGUgbWV0cmljcyBpbnZvbHZpbmcgdGhlIHN0YXR1cyBvZiBJQ1UgcGF0aWVudHMgYnUgYWxzbyB0aGUgbnVtYmVyIG9mIGN1cnJlbnQgaG9zcGl0YWxpemF0aW9ucy4gVGhlcmUgaXMgYWxzbyBhIHZhcmlhYmxlIGBvaF9yZWdpb25gIHdoaWNoIHNob3VsZCBkZW5vdGUgdGhlIGhlYWx0aCByZWdpb24gZnJvbSB3aGljaCB0aGUgZGF0YSBpcyBzYW1wbGVkLg0KDQpUaGUgNSByZWdpb25zIHJlcG9ydGVkIGNhbiB2YXJ5IGluIHNpemUgYW5kIHJlc291cmNlcyBidXQgd2UgY2FuIGNvbWJpbmUgdGhlc2UgcmVnaW9ucyBpbnRvIHNpbmdsZSB2YWx1ZXMgdG8gbG9vayBhdCB0aGUgb3ZlcmFsbCBudW1iZXIgb2YgaG9zcGl0YWxpemF0aW9ucyBvbiBhIGRhaWx5IGJhc2lzLiBUbyBhY2NvbXBsaXNoIHRoaXMgZmVhdCB3ZSdsbCB0dXJuIHRvIHRoZSBgZ3JvdXBfYnkoKWAgYW5kIGBzdW1tYXJpemUoKWAgZnVuY3Rpb25zLg0KDQpUaGUga2V5IHRvIHVzaW5nIHRoZXNlIGlzIHRvIGlkZW50aWZ5IHRoZSBnb2FscyBvZiB5b3VyIGFuYWx5c2lzLiBJbiB0aGUgY3VycmVudCBjYXNlLCB3ZSB3YW50IHRvIGNvbWJpbmUgYWxsIDUgaGVhbHRoIHJlZ2lvbnMgaW50byBhIHNpbmd1bGFyIG9uZSAqYmFzZWQgb24qIHRoZSBgZGF0ZWAgdmFyaWFibGUuIEZyb20gdGhlcmUgd2Ugd2lzaCB0byBjYWxjdWxhdGUgdGhlIGBzdW0oKWAgb2YgZWFjaCAqZ3JvdXAqIG9uIGEgdmFyaWFibGUgbGlrZSBgaG9zcGl0YWxpemF0aW9uc2AuDQoNCmBgYHtyfQ0KIyBQYXNzIHRoZSBob3NwaXRhbGl6YXRpb24gZGF0YSANCmNvdmlkX2hvc3BpdGFsaXphdGlvbnMgJT4lIA0KICAgICMgR3JvdXAgdGhlIGRhdGEgYnkgREFURQ0KICAgIGdyb3VwX2J5KC4uLikgJT4lIA0KICAgICMgU3VtbWFyaXplIGVhY2ggZ3JvdXAgYXMgYSBzdW0gb2YgImhvc3BpdGFsaXphdGlvbnMiDQogICAgc3VtbWFyaXplKC4uLikgJT4lIA0KDQogICAgIyBUYWtlIGEgbG9vayBhdCB0aGUgZGF0YQ0KICAgIGhlYWQoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNC43LjIgUGxvdCBvdXIgZGF0YSB1c2luZyB0aGUgc2FtZSBjb2RlDQoNCk5vdyB0aGF0IHdlIGtub3cgaG93IHRvIHN1bW1hcml6ZSB0aGUgZGF0YSwgd2UgY2FuIHdvcmsgb24gdmlzdWFsaW5nIGl0LiBGb3IgdGhlIHB1cnBvc2VzIG9mIGNvbXBhcmlzb24sIHdlIGNhbiByZXVzZSBvdXIgY29kZSBmcm9tIGJlZm9yZSBhbmQgc2ltcGx5IHN1YnN0aXR1dGUgaW4gdGhlIG5ldyBwYXJhbWV0ZXJzIGZvciBvdXIgdmlzdWFsaXphdGlvbnMgKGllIGB4YCBhbmQgYHlgIHZhbHVlcykuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQojIFBhc3MgdGhlIGhvc3BpdGFsaXphdGlvbiBkYXRhIA0KY292aWRfaG9zcGl0YWxpemF0aW9ucyAlPiUgDQogICAgIyBHcm91cCB0aGUgZGF0YSBieSBEQVRFDQogICAgZ3JvdXBfYnkoZGF0ZSkgJT4lIA0KICAgICMgU3VtbWFyaXplIGVhY2ggZ3JvdXAgYXMgYSBzdW0gb2YgImhvc3BpdGFsaXphdGlvbnMiDQogICAgc3VtbWFyaXplKGFsbF9jdXJyX2hvc3BpdGFsaXphdGlvbnMgPSBzdW0oaG9zcGl0YWxpemF0aW9ucykpICU+JSANCg0KIyByZWRpcmVjdCB0aGUgZmlsdGVyZWQgcmVzdWx0IHRvIGdncGxvdA0KZ2dwbG90KC4pICsNCiAgICAjIyMgMi4gQWVzdGhldGljczogdXBkYXRlIHRoZSB4IGFuZCB5IHNvdXJjZXMNCiAgICBhZXMoeCA9IC4uLiwgeSA9IC4uLikgKw0KDQogICAgdGhlbWVfYncoKSArICMgU2ltcGxpZnkgdGhlIHRoZW1lDQogICAgeGxhYigiRGF0ZSIpICsNCiAgICB5bGFiKCJDT1ZJRCBob3NwaXRhbGl6YXRpb25zIikgKw0KICAgIGdndGl0bGUoIkN1cnJlbnQgaG9zcGl0YWxpemF0aW9ucyBwZXIgZGF5IGFjcm9zcyBPbnRhcmlvIikgKw0KDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArICMgc2V0IHRleHQgc2l6ZQ0KICAgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleTk1IikpICsgIyBkYXJrZW4gb3VyIG1ham9yIHkgZ3JpZA0KICAgIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHJlbW92ZSBvdXIgbWlub3IgeSBncmlkDQogICAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArICMgcmVtb3ZlIG91ciBtaW5vciB4IGdyaWQNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSkgKyAjIHJvdGF0ZSBvdXIgeC1heGlzIHRleHQNCg0KICAgICMgMy4gU2NhbGluZw0KICAgICMgU3RhcnQgbG9va2luZyBhdCBkYXRhIGZyb20gTWFyY2ggMjAyMCBvbndhcmRzDQogICAgc2NhbGVfeF9kYXRlKGxpbWl0cyA9IGMoYXMuRGF0ZSgiMjAyMC0wMy0wMSIpLCBhcy5EYXRlKG1heChwaHVfd2luZG93X2RhdGEuZGYkc3RhcnRfZGF0ZSkpKSwNCiAgICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViLSVZIikgKw0KDQogICAgc2NhbGVfY29sb3JfdmlyaWRpc19kKCkgKyANCg0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX2xpbmUobGluZXdpZHRoPTEuNSkgKw0KDQogICAgIyBXaW50ZXIgMjAyMCBsb2NrZG93bg0KICAgIGdlb21fdGV4dChhZXMoeD1hcy5EYXRlKCIyMDIwLTEyLTI2IikgKyA3LCBsYWJlbCA9ICJQcm92aW5jZS13aWRlIGxvY2tkb3duIiwgeT0xODAwKSwgDQogICAgICAgICAgICAgIGFuZ2xlPTkwLCBoanVzdCA9IDAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInJlY3QiLCB4bWluPWFzLkRhdGUoIjIwMjAtMTItMjYiKSwgeG1heD1hcy5EYXRlKCIyMDIwLTEyLTI2IikgKyAxNCwgDQogICAgICAgICAgICAgeW1pbj0tSW5mLCB5bWF4PUluZiwgZmlsbD0icmVkIiwgYWxwaGE9MC4yKSArDQoNCiAgICAjIFNwcmluZyAyMDIxIExvY2tkb3duDQogICAgZ2VvbV90ZXh0KGFlcyh4PWFzLkRhdGUoIjIwMjEtMDQtMDMiKSArIDcsIGxhYmVsID0gIlByb3ZpbmNlLXdpZGUgbG9ja2Rvd24iLCB5PTE4MDApLCANCiAgICAgICAgICAgICAgYW5nbGU9OTAsIGhqdXN0ID0gMCwgc2l6ZT0xMCwgY29sb3VyPSJibGFjayIpICsNCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49YXMuRGF0ZSgiMjAyMS0wNC0wMyIpLCB4bWF4PWFzLkRhdGUoIjIwMjEtMDQtMDMiKSArIDE0LCANCiAgICAgICAgICAgICB5bWluPS1JbmYsIHltYXg9SW5mLCBmaWxsPSJyZWQiLCBhbHBoYT0wLjIpICsNCg0KICAgICMgT21pY3JvbiBhcnJpdmVzDQogICAgZ2VvbV90ZXh0KGFlcyh4PWFzLkRhdGUoIjIwMjEtMTEtMzAiKSwgbGFiZWwgPSAiRmlyc3QgT21pY3JvblxuY2FzZXMgcmVwb3J0ZWRcbmluIE9udGFyaW8iLCB5PTE1MDApLCANCiAgICAgICAgICAgICAgaGp1c3Q9MSwgdmp1c3QgPSAwLCBzaXplPTEwLCBjb2xvdXI9ImJsYWNrIikgKw0KICAgIGFubm90YXRlKCJzZWdtZW50IiwgeD1hcy5EYXRlKCIyMDIxLTExLTI4IiksIHhlbmQgPSBhcy5EYXRlKCIyMDIxLTExLTI4IiksDQogICAgICAgICAgICAgeT0xMjAwLCB5ZW5kPTUwMCwgY29sb3VyPSJyZWQiLCBsaW5ld2lkdGggPSAyLCBhcnJvdyA9IGFycm93KCkpICsNCg0KICAgICMgT250YXJpbyBlbmRzIHByb3BlciBQQ1IgdGVzdGluZw0KICAgIGdlb21fdGV4dChhZXMoeD1hcy5EYXRlKCIyMDIyLTAzLTAxIiksIGxhYmVsID0gIk9udGFyaW8gcmVkdWNlcyBwdWJsaWNcblBDUiBDT1ZJRC0xOSB0ZXN0aW5nIiwgeT0yNTAwKSwgDQogICAgICAgICAgICAgIGhqdXN0PTAsIHNpemU9MTAsIGNvbG91cj0iYmxhY2siKSArDQogICAgYW5ub3RhdGUoInNlZ21lbnQiLCB4PWFzLkRhdGUoIjIwMjItMDMtMDEiKSwgeGVuZCA9IGFzLkRhdGUoIjIwMjEtMTItMzEiKSwNCiAgICAgICAgICAgICB5PTI1MDAsIHllbmQ9MjUwMCwgY29sb3VyPSJyZWQiLCBsaW5ld2lkdGggPSAyLCBhcnJvdyA9IGFycm93KCkpIA0KYGBgDQoNCldlbGwgaXQgbG9va3MgbGlrZSBvdXIgaG9zcGl0YWxpemF0aW9uIGRhdGEgdGVsbHMgYSBkaWZmZXJlbnQgc3RvcnkgZnJvbSB0aGUgY2FzZSByZXBvcnQgZGF0YSEgU29tZXRoaW5nIHdvcnRoIGV4cGxvcmluZyBpbiB5b3VyIGFzc2lnbm1lbnQhDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDUuMC4wIENsYXNzIHN1bW1hcnkNCg0KVGhhdCdzIG91ciBmaXJzdCBjbGFzcyEgSWYgd2UndmUgbWFkZSBpdCB0aGlzIGZhciwgd2UndmUgcmV2aWV3ZWQgMS4gRm91bmRhdGlvbmFsIGNvbmNlcHRzIGluIFIgMi4gSGVscGZ1bCBmdW5jdGlvbnMgaW4gZ2VuZXJhdGluZyB0aWR5IGRhdGEgZm9yIGFuYWx5c2lzIDMuIEJhc2ljcyBvZiB2aXN1YWxpemF0aW9ucyB1c2luZyB0aGUgYGdncGxvdDJgDQoNCldlIHRvb2sgYSAibWVzc3kiIGRhdGFzZXQgZnJvbSB0aGUgT250YXJpbyBnb3Zlcm5tZW50IGFuZCBjcmVhdGVkIGEgdGlkeSBkYXRhIHNldCB0aGF0IHdlIHdlcmUgYWJsZSB0byB2aXN1YWxpemUuIFdlIHRvb2sgdGhhdCBmdXJ0aGVyIGJ5IHRyYW5zZm9ybWluZyB0aGUgZGF0YSBpbnRvIGEgMTQtZGF5IHNsaWRpbmcgd2luZG93IG9mIG1lYW4gbmV3IGNhc2VzIHBlciBkYXkgaW4gZWFjaCBwdWJsaWMgaGVhbHRoIHVuaXQuIFRoaXMgY2xhcmlmaWVkIG91ciBwaWN0dXJlIG9mIGNhc2VzIGFuZCB2aXN1YWxseSBjb25maXJtZWQgdGhhdCBzcHJlYWQgb2YgU0FSUy1Db1YtMiBkaWQgYXBwZWFyIHRvIGJlIG1pdGlnYXRlZCB0aHJvdWdoIGxvY2tkb3duIG9yZGVycy4NCg0KTmV4dCB3ZWVrPyBHZXR0aW5nIGRlZXBlciBpbnRvIGBnZ3Bsb3QyYCENCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDUuMS4wIFdlZWtseSBhc3NpZ25tZW50DQoNClRoaXMgd2VlaydzIGFzc2lnbm1lbnQgd2lsbCBiZSBmb3VuZCB1bmRlciB0aGUgY3VycmVudCBsZWN0dXJlIGZvbGRlciB1bmRlciB0aGUgImFzc2lnbm1lbnQiIHN1YmZvbGRlci4gSXQgd2lsbCBpbmNsdWRlIGFuIFIgbWFya2Rvd24gbm90ZWJvb2sgdGhhdCB5b3Ugd2lsbCB1c2UgdG8gcHJvZHVjZSB0aGUgY29kZSBhbmQgYW5zd2VycyBmb3IgdGhpcyB3ZWVrJ3MgYXNzaWdubWVudC4gUGxlYXNlIHByb3ZpZGUgYW5zd2VycyBpbiBtYXJrZG93biBvciBjb2RlIGNlbGxzIHRoYXQgaW1tZWRpYXRlbHkgZm9sbG93IGVhY2ggcXVlc3Rpb24gc2VjdGlvbi4NCg0KfCAgICAgICAgICAgICAgICAgICAgfCBBc3NpZ25tZW50IGJyZWFrZG93biB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgICAgICAgIENvZGUgICAgICAgIHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBEb2VzIGl0IGZvbGxvdyBiZXN0IHByYWN0aWNlcz8gICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gRG9lcyBpdCBtYWtlIGdvb2QgdXNlIG9mIGF2YWlsYWJsZSBwYWNrYWdlcz8gfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIFdhcyBkYXRhIHByZXBhcmVkIHByb3Blcmx5ICAgICAgICAgICAgICAgICAgIHwNCnwgQW5zd2VycyBhbmQgT3V0cHV0IHwgICAgICAgICA1MCUgICAgICAgICAgfCBcLSBJcyBvdXRwdXQgYmFzZWQgb24gdGhlIGNvcnJlY3QgZGF0YXNldD8gICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gQXJlIGdyb3VwaW5ncyBhcHByb3ByaWF0ZSAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIEFyZSBjb3JyZWN0IHRpdGxlcy9heGVzL2xlZ2VuZHMgY29ycmVjdD8gICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBJcyBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZ3JhcGhzIGNvcnJlY3Q/ICAgICB8DQoNClNpbmNlIGNvZGluZyBzdHlsZXMgYW5kIHNvbHV0aW9ucyBjYW4gZGlmZmVyLCBzdHVkZW50cyBhcmUgZW5jb3VyYWdlZCB0byB1c2UgYmVzdCBwcmFjdGljZXMuIEFzc2lnbm1lbnRzICptYXkqIGJlIHJld2FyZGVkIGZvciB3ZWxsLWNvZGVkIG9yIGVsZWdhbnQgc29sdXRpb25zLg0KDQpZb3UgY2FuIHNhdmUgYW5kIGRvd25sb2FkIHRoZSBKdXB5dGVyIG5vdGVib29rIGluIGl0cyBuYXRpdmUgZm9ybWF0LiBTdWJtaXQgdGhpcyBmaWxlIHRvIHRoZSB0aGUgYXBwcm9wcmlhdGUgYXNzaWdubWVudCBzZWN0aW9uIGJ5IDEyOjU5IHBtIG9uIHRoZSBkYXRlIG9mIG91ciBuZXh0IGNsYXNzOiBNYXJjaCAxNHRoLCAyMDI0Lg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS4yLjAgQWNrbm93bGVkZ2VtZW50cw0KDQoqKlJldmlzaW9uIDEuMC4wKio6IGNyZWF0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjFIIFMgTEVDMDE0MSoqLCAwMy0yMDIxIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMS4wLjEqKjogZWRpdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIwSCBTIExFQzAxNDEqKiwgMDMtMjAyMiBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDEuMC4yKio6IGVkaXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMEggUyBMRUMwMTQxKiosIDAzLTIwMjMgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAyLjAuMCoqOiBSZXZpc2VkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIwSCBTIExFQzAxNDEqKiwgMDMtMjAyNCBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNS4zLjAgUmVmZXJlbmNlcw0KDQoxLiAgQ29lcmNpb24gZnJvbSAiUiBpbiBhIE51dHNoZWxsIjogPGh0dHBzOi8vd3d3Lm9yZWlsbHkuY29tL2xpYnJhcnkvdmlldy9yLWluLWEvOTc4MTQ0OTM1ODIwNC9jaDA1czA4Lmh0bWw+DQoyLiAgVGliYmxlcyB2cyBkYXRhIGZyYW1lcyBmcm9tICJSU3R1ZGlvIGJsb2ciOiA8aHR0cHM6Ly9ibG9nLnJzdHVkaW8uY29tLzIwMTYvMDMvMjQvdGliYmxlLTEtMC0wLz4NCjMuICBJbmRleGluZyBlbGVtZW50cyBmcm9tIGEgdmVjdG9yIG9yIGxpc3Q6IDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9kb2MvbWFudWFscy9SLWxhbmcuaHRtbCNJbmRleGluZz4NCjQuICBDaGFuZ2UgdGhlIGxldmVscyBvZiBhIGZhY3RvcjogPGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vTWFuaXB1bGF0aW5nX2RhdGEvQ2hhbmdpbmdfdGhlX29yZGVyX29mX2xldmVsc19vZl9hX2ZhY3Rvci8+DQo1LiAgVGhlIGFwcGx5IGZhbWlseSBvZiBmdW5jdGlvbnM6IDxodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8yMDE1LzA3L3ItdHV0b3JpYWwtb24tdGhlLWFwcGx5LWZhbWlseS1vZi1mdW5jdGlvbnMvPg0KNi4gIFRpZHkgZGF0YSBwcmluY2lwbGVzOiA8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RpZHlyL3ZpZ25ldHRlcy90aWR5LWRhdGEuaHRtbD4NCjcuICBVc2luZyB0aGUgYGx1YnJpZGF0ZWAgcGFja2FnZTogPGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZGF0ZXMtYW5kLXRpbWVzLmh0bWw+DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDYuMC4wIEFwcGVuZGl4IDE6IEluc3RydWN0aW9ucyBmb3IgaW5zdGFsbGluZyB5b3VyIG93biBzb2Z0d2FyZQ0KDQojIyA2LjEuMCBSIGFuZCBSU3R1ZGlvDQoNCiMjIyA2LjEuMSBJbnN0YWxsaW5nIFINCg0KQXMgb2YgMjAyMi0wMy0wMSwgdGhlIGxhdGVzdCBzdGFibGUgUiB2ZXJzaW9uIGlzIDQuMi4xOg0KDQpXaW5kb3dzOlwNCi0gR28gdG8gPGh0dHA6Ly9jcmFuLnV0c3RhdC51dG9yb250by5jYS8+XA0KLSBDbGljayBvbiAnRG93bmxvYWQgUiBmb3IgV2luZG93cydcDQotIENsaWNrIG9uICdpbnN0YWxsIFIgZm9yIHRoZSBmaXJzdCB0aW1lJ1wNCi0gQ2xpY2sgb24gJ0Rvd25sb2FkIFIgNC4yLjEgZm9yIFdpbmRvd3MnIChvciBhIG5ld2VyIHZlcnNpb24pXA0KLSBEb3VibGUtY2xpY2sgb24gdGhlIC5leGUgZmlsZSBvbmNlIGl0IGhhcyBkb3dubG9hZGVkIGFuZCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucy4NCg0KKE1hYykgT1MgWDpcDQotIEdvIHRvIDxodHRwOi8vY3Jhbi51dHN0YXQudXRvcm9udG8uY2EvPlwNCi0gQ2xpY2sgb24gJ0Rvd25sb2FkIFIgZm9yIChNYWMpIE9TIFgnXA0KLSBDbGljayBvbiBSLTQuMi4xIC5wa2cgKG9yIGEgbmV3ZXIgdmVyc2lvbilcDQotIE9wZW4gdGhlIC5wa2cgZmlsZSBvbmNlIGl0IGhhcyBkb3dubG9hZGVkIGFuZCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucy4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCkxpbnV4OlwNCi0gT3BlbiBhIHRlcm1pbmFsIChDdHJsICsgYWx0ICsgdCkgLSBzdWRvIGFwdC1nZXQgdXBkYXRlXA0KLSBzdWRvIGFwdC1nZXQgaW5zdGFsbCByLWJhc2VcDQotIHN1ZG8gYXB0LWdldCBpbnN0YWxsIHItYmFzZS1kZXYgKHNvIHlvdSBjYW4gY29tcGlsZSBwYWNrYWdlcyBmcm9tIHNvdXJjZSkNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA2LjEuMiBJbnN0YWxsaW5nIFJTdHVkaW8NCg0KQXMgb2YgMjAyMy0wMy0wMSwgdGhlIGxhdGVzdCBSU3R1ZGlvIHZlcnNpb24gaXMgMjAyMi4xMi4wKzM1MyAocmVsZWFzZWQgMjAyMi0xMi0xNSkNCg0KV2luZG93cyAoMTAvMTEpOlwNCi0gR28gdG8gPGh0dHBzOi8vcG9zaXQuY28vZG93bmxvYWRzLz5cDQotIENsaWNrIG9uICdSU1RVRElPLTIwMjIuMTIuMC0zNTMuRVhFJyB0byBkb3dubG9hZCB0aGUgaW5zdGFsbGVyIChvciBhIG5ld2VyIHZlcnNpb24pXA0KLSBEb3VibGUtY2xpY2sgb24gdGhlIC5leGUgZmlsZSBvbmNlIGl0IGhhcyBkb3dubG9hZGVkIGFuZCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucy4NCg0KKE1hYykgT1MgWCAoMTErKTpcDQotIEdvIHRvIDxodHRwczovL3Bvc2l0LmNvL2Rvd25sb2Fkcy8+XA0KLSBDbGljayBvbiAnUlNUVURJTy0yMDIyLjEyLjAtMzUzLkRNRycgdG8gZG93bmxvYWQgdGhlIGluc3RhbGxlciAob3IgYSBuZXdlciB2ZXJzaW9uKVwNCi0gRG91YmxlLWNsaWNrIG9uIHRoZSAuZG1nIGZpbGUgb25jZSBpdCBoYXMgZG93bmxvYWRlZCBhbmQgZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpMaW51eDpcDQotIEdvIHRvIDxodHRwczovL3Bvc2l0LmNvL2Rvd25sb2Fkcy8+XA0KLSBDbGljayBvbiB0aGUgaW5zdGFsbGVyIHRoYXQgZGVzY3JpYmVzIHlvdXIgTGludXggZGlzdHJpYnV0aW9uLCBlLmcuICdSU1RVRElPLTIwMjIuMTIuMC0zNTMtQU1ENjQuREVCJyAob3IgYSBuZXdlciB2ZXJzaW9uKVwNCi0gRG91YmxlLWNsaWNrIG9uIHRoZSAuZGViIGZpbGUgb25jZSBpdCBoYXMgZG93bmxvYWRlZCBhbmQgZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMuXA0KLSBJZiBkb3VibGUtY2xpY2tpbmcgb24geW91ciAuZGViIGZpbGUgZGlkIG5vdCBvcGVuIHRoZSBzb2Z0d2FyZSBtYW5hZ2VyLCBvcGVuIHRoZSB0ZXJtaW5hbCAoQ3RybCArIGFsdCArIHQpIGFuZCB0eXBlICoqc3VkbyBkcGtnIC1pIC9wYXRoL3RvL2luc3RhbGxlci9SU1RVRElPLTIwMjIuMTIuMC0zNTMtQU1ENjQuZGViKioNCg0KYGBgICAgICAgICAgDQogX05vdGU6IFlvdSBoYXZlIDMgdGhpbmdzIHRoYXQgY291bGQgY2hhbmdlIGluIHRoaXMgbGFzdCBjb21tYW5kLl8gICAgIA0KIDEuIFRoaXMgYXNzdW1lcyB5b3UgaGF2ZSBqdXN0IG9wZW5lZCB0aGUgdGVybWluYWwgYW5kIGFyZSBpbiB5b3VyIGhvbWUgZGlyZWN0b3J5LiAoSWYgbm90LCB5b3UgaGF2ZSB0byBtb2RpZnkgeW91ciBwYXRoLiBZb3UgY2FuIGdldCB0byB5b3VyIGhvbWUgZGlyZWN0b3J5IGJ5IHR5cGluZyBjZCB+LikgICAgIA0KIDIuIFRoaXMgYXNzdW1lcyB5b3UgaGF2ZSBkb3dubG9hZGVkIHRoZSAuZGViIGZpbGUgdG8gRG93bmxvYWRzLiAoSWYgeW91IGRvd25sb2FkZWQgdGhlIGZpbGUgc29tZXdoZXJlIGVsc2UsIHlvdSBoYXZlIHRvIGNoYW5nZSB0aGUgcGF0aCB0byB0aGUgZmlsZSwgb3IgZG93bmxvYWQgdGhlIC5kZWIgZmlsZSB0byBEb3dubG9hZHMpLiAgICAgIA0KIDMuIFRoaXMgYXNzdW1lcyB5b3VyIGZpbGUgbmFtZSBmb3IgLmRlYiBpcyB0aGUgc2FtZSBhcyBhYm92ZS4gKFB1dCB0aGUgbmFtZSBtYXRjaGluZyB0aGUgLmRlYiBmaWxlIHlvdSBkb3dubG9hZGVkKS4NCmBgYA0KDQpJZiB5b3UgaGF2ZSBhIHByb2JsZW0gd2l0aCBpbnN0YWxsaW5nIFIgb3IgUlN0dWRpbywgeW91IGNhbiBhbHNvIHRyeSB0byBzb2x2ZSB0aGUgcHJvYmxlbSB5b3Vyc2VsZiBieSBHb29nbGluZyBhbnkgZXJyb3IgbWVzc2FnZXMgeW91IGdldC4gWW91IGNhbiBhbHNvIHRyeSB0byBnZXQgaW4gdG91Y2ggd2l0aCBtZSBvciB0aGUgY291cnNlIFRBcy4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA2LjEuMyBHZXR0aW5nIHRvIGtub3cgdGhlIFJTdHVkaW8gZW52aXJvbm1lbnQNCg0KUlN0dWRpbyBpcyBhbiBJREUgKEludGVncmF0ZWQgRGV2ZWxvcG1lbnQgRW52aXJvbm1lbnQpIGZvciBSIHRoYXQgcHJvdmlkZXMgYSBtb3JlIHVzZXItZnJpZW5kbHkgZXhwZXJpZW5jZSB0aGFuIHVzaW5nIFIgaW4gYSB0ZXJtaW5hbCBzZXR0aW5nLiBJdCBoYXMgNCBtYWluIGFyZWFzIG9yIHBhbmVzLCB3aGljaCB5b3UgY2FuIGN1c3RvbWl6ZSB0byBzb21lIGV4dGVudCB1bmRlciBgVG9vbHMgPiBHbG9iYWwgT3B0aW9ucyA+IFBhbmUgTGF5b3V0YDoNCg0KMS4gICoqU291cmNlKiogLSBUaGUgY29kZSB5b3UgYXJlIGFubm90YXRpbmcgYW5kIGtlZXBpbmcgaW4geW91ciBzY3JpcHQuDQoyLiAgKipDb25zb2xlKiogLSBXaGVyZSB5b3VyIGNvZGUgaXMgZXhlY3V0ZWQuDQozLiAgKipFbnZpcm9ubWVudCoqIC0gV2hhdCBnbG9iYWwgb2JqZWN0cyB5b3UgaGF2ZSBjcmVhdGVkIGFuZCBmdW5jdGlvbnMgeW91IGhhdmUgd3JpdHRlbi9zb3VyY2VkLlwNCiAgICBIaXN0b3J5IC0gQSByZWNvcmQgb2YgYWxsIHRoZSBjb2RlIHlvdSBoYXZlIGV4ZWN1dGVkIGluIHRoZSBjb25zb2xlLlwNCiAgICBDb25uZWN0aW9ucyAtIFdoaWNoIGRhdGEgc291cmNlcyB5b3UgYXJlIGNvbm5lY3RpbmcgdG8uIChOb3QgYmVpbmcgdXNlZCBpbiB0aGlzIGNvdXJzZS4pDQo0LiAgKipGaWxlcywgUGxvdHMsIFBhY2thZ2VzLCBIZWxwLCBWaWV3ZXIqKiAtIHNlbGYtZXhwbGFuYXRvcnlpc2ggaWYgeW91IGNsaWNrIG9uIHRoZWlyIHRhYnMuDQoNCkFsbCBvZiB0aGUgcGFuZXMgY2FuIGJlIG1pbmltaXplZCBvciBtYXhpbWl6ZWQgdXNpbmcgdGhlIGxhcmdlIGFuZCBzbWFsbCBib3ggb3V0bGluZXMgaW4gdGhlIHRvcCByaWdodCBvZiBlYWNoIHBhbmUuDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovUl9zdHVkaW9fZGVmYXVsdF9sYXlvdXQuanBnP3Jhdz10cnVlIiB3aWR0aD0iNzAwIi8+DQo6OjoNCg0KIyMjIDYuMS4zLjEgU291cmNlDQoNClRoZSAqKlNvdXJjZSoqIGlzIHdoZXJlIHlvdSBhcmUga2VlcGluZyB0aGUgY29kZSBhbmQgYW5ub3RhdGlvbiB0aGF0IHlvdSB3YW50IHRvIGJlIHNhdmVkIGFzIHlvdXIgc2NyaXB0LiBUaGUgdGFiIGF0IHRoZSB0b3AgbGVmdCBvZiB0aGUgcGFuZSBoYXMgeW91ciBzY3JpcHQgbmFtZSAoaS5lLiAnVW50aXRsZWQuUicpLCBhbmQgeW91IGNhbiBzd2l0Y2ggYmV0d2VlbiBzY3JpcHRzIGJ5IHRvZ2dsaW5nIHRoZSB0YWJzLiBZb3UgY2FuIHNhdmUsIHNlYXJjaCBvciBwdWJsaXNoIHlvdXIgc291cmNlIGNvZGUgdXNpbmcgdGhlIGJ1dHRvbnMgYWxvbmcgdGhlIHBhbmUgaGVhZGVyLiBDb2RlIGluIHRoZSBTb3VyY2UgcGFuZSBpcyAqcnVuKiBvciAqZXhlY3V0ZWQqIGF1dG9tYXRpY2FsbHkuDQoNClRvIHJ1biB5b3VyIGN1cnJlbnQgbGluZSBvZiBjb2RlIG9yIGEgaGlnaGxpZ2h0ZWQgc2VnbWVudCBvZiBjb2RlIGZyb20gdGhlIFNvdXJjZSBwYW5lIHlvdSBjYW46XA0KYSkgY2xpY2sgdGhlIGJ1dHRvbiBgJ1J1bicgLT4gJ1J1biBTZWxlY3RlZCBMaW5lKHMpJ2AsXA0KYikgY2xpY2sgYCdDb2RlJyAtPiAnUnVuIFNlbGVjdGVkIExpbmUocyknYCBmcm9tIHRoZSBtZW51IGJhcixcDQpjKSB1c2UgdGhlIGtleWJvYXJkIHNob3J0Y3V0IGBDVFJMICsgRU5URVJgIChXaW5kb3dzICYgTGludXgpIGBDb21tYW5kICsgRU5URVJgIChNYWMpIChyZWNvbW1lbmRlZCksXA0KZCkgY29weSBhbmQgcGFzdGUgeW91ciBjb2RlIGludG8gdGhlIENvbnNvbGUgYW5kIGhpdCBgRW50ZXJgIChub3QgcmVjb21tZW5kZWQpLg0KDQpUaGVyZSBhcmUgYWx3YXlzIG1hbnkgd2F5cyB0byBkbyB0aGluZ3MgaW4gUiwgYnV0IHRoZSBmYXN0ZXN0IHdheSB3aWxsIGFsd2F5cyBiZSB0aGUgb3B0aW9uIHRoYXQga2VlcHMgeW91ciBoYW5kcyBvbiB0aGUga2V5Ym9hcmQuDQoNCiMjIyA2LjEuMy4yIENvbnNvbGUNCg0KWW91IGNhbiBhbHNvIHR5cGUgYW5kIGV4ZWN1dGUgeW91ciBjb2RlIChieSBoaXR0aW5nIGBFTlRFUmApIGluIHRoZSAqKkNvbnNvbGUqKiB3aGVuIHRoZSBgPmAgcHJvbXB0IGlzIHZpc2libGUuIElmIHlvdSBlbnRlciBjb2RlIGFuZCB5b3Ugc2VlIGEgYCtgIGluc3RlYWQgb2YgYSBwcm9tcHQsIFIgZG9lc24ndCB0aGluayB5b3UgYXJlIGZpbmlzaGVkIGVudGVyaW5nIGNvZGUgKGkuZS4geW91IG1pZ2h0IGJlIG1pc3NpbmcgYSBicmFja2V0KS4gSWYgdGhpcyBpc24ndCBpbW1lZGlhdGVseSBmaXhhYmxlLCB5b3UgY2FuIGhpdCBgRXNjYCB0d2ljZSB0byBnZXQgYmFjayB0byB5b3VyIHByb21wdC4gVXNpbmcgdGhlIHVwIGFuZCBkb3duIGFycm93IGtleXMsIHlvdSBjYW4gZmluZCBwcmV2aW91cyBjb21tYW5kcyBpbiB0aGUgQ29uc29sZSBpZiB5b3Ugd2FudCB0byByZXJ1biBjb2RlIG9yIGZpeCBhbiBlcnJvciByZXN1bHRpbmcgZnJvbSBhIHR5cG8uDQoNCk9uIHRoZSBDb25zb2xlIHRhYiBpbiB0aGUgdG9wIGxlZnQgb2YgdGhhdCBwYW5lIGlzIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4gUHJlc3NpbmcgdGhlIGFycm93IG5leHQgdG8geW91ciB3b3JraW5nIGRpcmVjdG9yeSB3aWxsIG9wZW4geW91ciBjdXJyZW50IGZvbGRlciBpbiB0aGUgRmlsZXMgcGFuZS4gSWYgeW91IGZpbmQgeW91ciBDb25zb2xlIGlzIGdldHRpbmcgdG9vIGNsdXR0ZXJlZCwgc2VsZWN0aW5nIHRoZSBicm9vbSBpY29uIGluIHRoYXQgcGFuZSB3aWxsIGNsZWFyIGl0IGZvciB5b3UuIFRoZSBDb25zb2xlIGFsc28gc2hvd3MgaW5mb3JtYXRpb246IHVwb24gc3RhcnQgdXAgYWJvdXQgUiAoc3VjaCBhcyB2ZXJzaW9uIG51bWJlciksIGR1cmluZyB0aGUgaW5zdGFsbGF0aW9uIG9mIHBhY2thZ2VzLCB3aGVuIHRoZXJlIGFyZSB3YXJuaW5ncywgYW5kIHdoZW4gdGhlcmUgYXJlIGVycm9ycy4NCg0KIyMjIDYuMS4zLjMgRW52aXJvbm1lbnQNCg0KSW4gdGhlICoqR2xvYmFsIEVudmlyb25tZW50KiogeW91IGNhbiBzZWUgYWxsIG9mIHRoZSBzdG9yZWQgb2JqZWN0cyB5b3UgaGF2ZSBjcmVhdGVkIG9yIHNvdXJjZWQgKGltcG9ydGVkIGZyb20gYW5vdGhlciBzY3JpcHQpLiBUaGUgR2xvYmFsIEVudmlyb25tZW50IGNhbiBiZWNvbWUgY2x1dHRlcmVkLCBzbyBpdCBhbHNvIGhhcyBhIGJyb29tIGJ1dHRvbiB0byBjbGVhciBpdHMgd29ya3NwYWNlLg0KDQoqKk9iamVjdHMqKiBhcmUgbWFkZSBieSB1c2luZyB0aGUgYXNzaWdubWVudCBvcGVyYXRvciBgPC1gLiBPbiB0aGUgbGVmdCBzaWRlIG9mIHRoZSBhcnJvdywgeW91IGhhdmUgdGhlIG5hbWUgb2YgeW91ciBvYmplY3QuIE9uIHRoZSByaWdodCBzaWRlIHlvdSBoYXZlIHdoYXQgeW91IGFyZSBhc3NpZ25pbmcgdG8gdGhhdCBvYmplY3QuIEluIHRoaXMgc2Vuc2UsIHlvdSBjYW4gdGhpbmsgb2YgYW4gb2JqZWN0IGFzIGEgY29udGFpbmVyLiBUaGUgY29udGFpbmVyIGhvbGRzIHRoZSB2YWx1ZXMgZ2l2ZW4gYXMgd2VsbCBhcyBpbmZvcm1hdGlvbiBhYm91dCAnY2xhc3MnIGFuZCAnbWV0aG9kcycgKHdoaWNoIHdlIHdpbGwgY29tZSBiYWNrIHRvKS4NCg0KVHlwZSBgeCA8LSBjKDIsNClgIGluIHRoZSBDb25zb2xlIGZvbGxvd2VkIGJ5IGBFbnRlcmAuIDFEIG9iamVjdHMnIGRhdGEgdHlwZXMgY2FuIGJlIHNlZW4gaW1tZWRpYXRlbHkgYXMgd2VsbCBhcyB0aGVpciBmaXJzdCBmZXcgdmFsdWVzLiBOb3cgdHlwZSBgeSA8LSBkYXRhLmZyYW1lKG51bWJlcnMgPSBjKDEsMiwzKSwgbGV0dGVycyA9IGMoImEiLCJiIiwiYyIpKWAgaW4gdGhlIENvbnNvbGUgZm9sbG93ZWQgYnkgYEVudGVyYC4gWW91IGNhbiBpbW1lZGlhdGVseSBzZWUgdGhlIGRpbWVuc2lvbiBvZiAyRCBvYmplY3RzLCBhbmQgeW91IGNhbiBjaGVjayB0aGUgc3RydWN0dXJlIG9mIGRhdGEgZnJhbWVzIGFuZCBsaXN0cyAobW9yZSBsYXRlcikgYnkgY2xpY2tpbmcgb24gdGhlIG9iamVjdCdzIGFycm93LiBDbGlja2luZyBvbiB0aGUgb2JqZWN0IG5hbWUgd2lsbCBvcGVuIHRoZSBvYmplY3QgdG8gdmlldyBpbiBhIG5ldyB0YWIuIEN1c3RvbSBmdW5jdGlvbnMgY3JlYXRlZCBpbiBzZXNzaW9uIG9yIHNvdXJjZWQgd2lsbCBhbHNvIGFwcGVhciBpbiB0aGlzIHBhbmUuDQoNClRoZSBFbnZpcm9ubWVudCBwYW5lIGRyb3Bkb3duIGRpc3BsYXlzIGFsbCBvZiB0aGUgY3VycmVudGx5IGxvYWRlZCBwYWNrYWdlcyBpbiBhZGRpdGlvbiB0byB0aGUgR2xvYmFsIEVudmlyb25tZW50LiAqTG9hZGVkKiBtZWFucyB0aGF0IGFsbCBvZiB0aGUgdG9vbHMvZnVuY3Rpb25zIGluIHRoZSBwYWNrYWdlIGFyZSBhdmFpbGFibGUgZm9yIHVzZS4gUiBjb21lcyB3aXRoIGEgbnVtYmVyIG9mIHBhY2thZ2VzIHByZS1sb2FkZWQgKGkuZS4gYmFzZSwgZ3JEZXZpY2VzKS4NCg0KSW4gdGhlIEhpc3RvcnkgdGFiIGFyZSBhbGwgb2YgdGhlIGNvbW1hbmRzIHlvdSBoYXZlIGV4ZWN1dGVkIGluIHRoZSBDb25zb2xlIGR1cmluZyB5b3VyIHNlc3Npb24uIFlvdSBjYW4gc2VsZWN0IGEgbGluZSBvZiBjb2RlIGFuZCBzZW5kIGl0IHRvIHRoZSBTb3VyY2Ugb3IgQ29uc29sZS4NCg0KVGhlIENvbm5lY3Rpb25zIHRhYiBpcyB0byBjb25uZWN0IHRvIGRhdGEgc291cmNlcyBzdWNoIGFzIFNwYXJrIGFuZCB3aWxsIG5vdCBiZSB1c2VkIGluIHRoaXMgbGVzc29uLg0KDQojIyMgNi4xLjMuNCBGaWxlcywgUGxvdHMsIFBhY2thZ2VzLCBIZWxwLCBWaWV3ZXINCg0KVGhlIEZpbGVzIHRhYiBhbGxvd3MgeW91IHRvIHNlYXJjaCB0aHJvdWdoIGRpcmVjdG9yaWVzOyB5b3UgY2FuIGdvIHRvIG9yIHNldCB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5IGJ5IG1ha2luZyB0aGUgYXBwcm9wcmlhdGUgc2VsZWN0aW9uIHVuZGVyIHRoZSBgTW9yZWAgKGJsdWUgZ2VhcikgZHJvcC1kb3duIG1lbnUuIFRoZSBgLi4uYCB0byB0aGUgdG9wIGxlZnQgb2YgdGhlIHBhbmUgYWxsb3dzIHlvdSB0byBzZWFyY2ggZm9yIGEgZm9sZGVyIGluIGEgbW9yZSB0cmFkaXRpb25hbCBtYW5uZXIuDQoNClRoZSBQbG90cyB0YWIgaXMgd2hlcmUgcGxvdHMgeW91IG1ha2UgaW4gYSAuUiBzY3JpcHQgd2lsbCBhcHBlYXIgKG5vdGVib29rcyBhbmQgbWFya2Rvd24gcGxvdHMgd2lsbCBiZSBzaG93biBpbiB0aGUgU291cmNlIHBhbmUpLiBUaGVyZSBpcyB0aGUgb3B0aW9uIHRvIEV4cG9ydCBhbmQgc2F2ZSB0aGVzZSBwbG90cyBtYW51YWxseS4NCg0KVGhlIFBhY2thZ2VzIHRhYiBoYXMgYWxsIG9mIHRoZSBwYWNrYWdlcyB0aGF0IGFyZSBpbnN0YWxsZWQgYW5kIHRoZWlyIHZlcnNpb25zLCBhbmQgYnV0dG9ucyB0byBJbnN0YWxsIG9yIFVwZGF0ZSBwYWNrYWdlcy4gQSBjaGVjayBtYXJrIGluIHRoZSBib3ggbmV4dCB0byB0aGUgcGFja2FnZSBtZWFucyB0aGF0IHRoZSBwYWNrYWdlIGlzIGxvYWRlZC4gWW91IGNhbiBsb2FkIGEgcGFja2FnZSBieSBhZGRpbmcgYSBjaGVjayBtYXJrIG5leHQgdG8gYSBwYWNrYWdlLCBob3dldmVyIGl0IGlzIGdvb2QgcHJhY3RpY2UgdG8gaW5zdGVhZCBsb2FkIHRoZSBwYWNrYWdlIGluIHlvdXIgc2NyaXB0IHRvIGFpZCBpbiByZXByb2R1Y2liaWxpdHkuDQoNClRoZSBIZWxwIG1lbnUgaGFzIHRoZSBkb2N1bWVudGF0aW9uIGZvciBhbGwgcGFja2FnZXMgYW5kIGZ1bmN0aW9ucy4gRm9yIGVhY2ggZnVuY3Rpb24geW91IHdpbGwgZmluZCBhIGRlc2NyaXB0aW9uIG9mIHdoYXQgdGhlIGZ1bmN0aW9uIGRvZXMsIHRoZSBhcmd1bWVudHMgaXQgdGFrZXMsIHdoYXQgdGhlIGZ1bmN0aW9uIGRvZXMgdG8gdGhlIGlucHV0cyAoZGV0YWlscyksIHdoYXQgaXQgb3V0cHV0cywgYW5kIGFuIGV4YW1wbGUuIFNvbWUgb2YgdGhlIGhlbHAgZG9jdW1lbnRhdGlvbiBpcyBkaWZmaWN1bHQgdG8gcmVhZCBvciBsZXNzIHRoYW4gY29tcHJlaGVuc2l2ZSwgaW4gd2hpY2ggY2FzZSBnb2dnbGluZyB0aGUgZnVuY3Rpb24gaXMgYSBnb29kIGlkZWEuDQoNClRoZSBWaWV3ZXIgd2lsbCBkaXNwbGF5IHZpZ25ldHRlcywgb3IgbG9jYWwgd2ViIGNvbnRlbnQgc3VjaCBhcyBhIFNoaW55IGFwcCwgaW50ZXJhY3RpdmUgZ3JhcGhzLCBvciBhIHJlbmRlcmVkIGh0bWwgZG9jdW1lbnQuDQoNCiMjIyA2LjEuMy41IEdsb2JhbCBPcHRpb25zDQoNCkkgc3VnZ2VzdCB5b3UgdGFrZSBhIGxvb2sgYXQgYFRvb2xzIC0+IEdsb2JhbCBPcHRpb25zYCB0byBjdXN0b21pemUgeW91ciBleHBlcmllbmNlLg0KDQpGb3IgZXhhbXBsZSwgdW5kZXIgYENvZGUgLT4gRWRpdGluZ2AgSSBoYXZlIHNlbGVjdGVkIGBTb2Z0LXdyYXAgUiBzb3VyY2UgZmlsZXNgIGZvbGxvd2VkIGJ5IGBBcHBseWAgc28gdGhhdCBteSB0ZXh0IHdpbGwgd3JhcCBieSBpdHNlbGYgd2hlbiBJIGFtIHR5cGluZyBhbmQgbm90IGNyZWF0ZSBhIGxvbmcgbGluZSBvZiB0ZXh0Lg0KDQpZb3UgbWF5IGFsc28gd2FudCB0byBjaGFuZ2UgdGhlIGBBcHBlYXJhbmNlYCBvZiB5b3VyIGNvZGUuIEkgbGlrZSB0aGUgYFJTdHVkaW8gdGhlbWU6IE1vZGVybmAgYW5kIGBFZGl0b3IgZm9udDogVWJ1bnR1IE1vbm9gLCBidXQgcGljayB3aGF0ZXZlciB5b3UgbGlrZSEgQWdhaW4sIHlvdSBuZWVkIHRvIGhpdCBgQXBwbHlgIHRvIG1ha2UgY2hhbmdlcy4NCg0KVGhhdCB3aGlybHdpbmQgdG91ciBpc24ndCBldmVyeXRoaW5nIHRoZSBJREUgY2FuIGRvLCBidXQgaXQgaXMgZW5vdWdoIHRvIGdldCBzdGFydGVkLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgVGhlIENlbnRlciBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikNCg0KVGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikgYXQgdGhlIFVuaXZlcnNpdHkgb2YgVG9yb250byBvZmZlcnMgY29tcHJlaGVuc2l2ZSBleHBlcmltZW50YWwgZGVzaWduLCByZXNlYXJjaCwgYW5kIGFuYWx5c2lzIHNlcnZpY2VzIGluIG1pY3JvYmlvbWUgYW5kIG1ldGFnZW5vbWljIHN0dWRpZXMsIGdlbm9taWNzLCBwcm90ZW9taWNzLCBhbmQgYmlvaW5mb3JtYXRpY3MuDQoNCkZyb20gdGFyZ2V0ZWQgRE5BIGFtcGxpY29uIHNlcXVlbmNpbmcgdG8gdHJhbnNjcmlwdG9tZXMsIHdob2xlIGdlbm9tZXMsIGFuZCBtZXRhZ2Vub21lcywgZnJvbSBwcm90ZWluIGlkZW50aWZpY2F0aW9uIHRvIHBvc3QtdHJhbnNsYXRpb25hbCBtb2RpZmljYXRpb24sIENBR0VGIGhhcyB0aGUgdG9vbHMgYW5kIGtub3dsZWRnZSB0byBzdXBwb3J0IHlvdXIgcmVzZWFyY2guIE91ciBzdGF0ZS1vZi10aGUtYXJ0IGZhY2lsaXR5IGFuZCBleHBlcmllbmNlZCByZXNlYXJjaCBzdGFmZiBwcm92aWRlIGEgYnJvYWQgcmFuZ2Ugb2Ygc2VydmljZXMsIGluY2x1ZGluZyBib3RoIHN0YW5kYXJkIGFuYWx5c2VzIGFuZCB0ZWNobmlxdWVzIGRldmVsb3BlZCBieSBvdXIgdGVhbS4gSW4gcGFydGljdWxhciwgd2UgaGF2ZSBzcGVjaWFsIGV4cGVydGlzZSBpbiBtaWNyb2JpYWwsIHBsYW50LCBhbmQgZW52aXJvbm1lbnRhbCBzeXN0ZW1zLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB1cyBhbmQgdGhlIHNlcnZpY2VzIHdlIG9mZmVyLCBwbGVhc2UgdmlzaXQgPGh0dHBzOi8vd3d3LmNhZ2VmLnV0b3JvbnRvLmNhLz4uDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovQ0FHRUZfbmV3LnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQo=